+ Start a Discussion
jwolfjwolf 

How to test asynchronous apex?

What is the best way to test methods annotated with @future? I have one such method that updates account records, but I am unable to write any meaningful tests, because I can't wait until it is finished executing to test the updated rows. Does anyone have any suggesstions?
Best Answer chosen by Admin (Salesforce Developers) 
aalbertaalbert

As you noticed, the @future method will execute so you will get the code coverage, but it will not execute in a synchronous order. The best way to "assert" the @future method operations is to wrap your test scenario with Test.startTest() and Test.stopTest(). The @future method will execute before the Test.stopTest() method is called, therefore, you can assert the entire test results after the Test.stopTest() is executed.

 

Below a simple example. Notice the SOQL query to get the Account and assert that it was created is AFTER the Test.stopTest() method. 

 

global testCls {

@future Public static void myMethod(){
System.debug('inside myMethod...');
Account a = new Account(name='testing future annotation');
insert a;
System.debug('exiting myMethod...');

}


static testMethod void myTest() {

Test.startTest();
testCls.myMethod();
System.debug('running myTest..');
Test.stopTest();
Account[] a = [select id from account where name='testing future annotation'];
System.debug('size: ' + a.size());
}

}

 

All Answers

aalbertaalbert

As you noticed, the @future method will execute so you will get the code coverage, but it will not execute in a synchronous order. The best way to "assert" the @future method operations is to wrap your test scenario with Test.startTest() and Test.stopTest(). The @future method will execute before the Test.stopTest() method is called, therefore, you can assert the entire test results after the Test.stopTest() is executed.

 

Below a simple example. Notice the SOQL query to get the Account and assert that it was created is AFTER the Test.stopTest() method. 

 

global testCls {

@future Public static void myMethod(){
System.debug('inside myMethod...');
Account a = new Account(name='testing future annotation');
insert a;
System.debug('exiting myMethod...');

}


static testMethod void myTest() {

Test.startTest();
testCls.myMethod();
System.debug('running myTest..');
Test.stopTest();
Account[] a = [select id from account where name='testing future annotation'];
System.debug('size: ' + a.size());
}

}

 

This was selected as the best answer
jwolfjwolf
Thank you for your response. That was very helpful.
GoForceGoGoForceGo

My @future code executes an httpRequest and hence Apex does not allow that.

 

I am using the following approach:

 

http://wiki.developerforce.com/index.php/An_Introduction_to_Apex_Code_Test_Methods#Test_Methods_and_Apex_Callouts

 

I am returning fake http responses if I am inside a testMethod.

 

Now how do I tell in Apex if I am inside a testMethod ? I don't see - so I just hacked it by adding a "inside_test_method__c" custom field to the objects of interest. In my test method, I create objects with this variable set as true...

 

Is there a way in Apex to tell if I am in a test method?

 

 

 

aalbertaalbert

You don't need to create a custom field to specify if you are in a testMethod. But you can implement a similar functionality just using a boolean variable in your apex class that is set to true only within a testMethod.

 

GoForceGoGoForceGo

You are right - I think I can do it in this case. 

 

Interestingly, I have @future code is invoked from a trigger. I will just have to create a dummy class where I set testMethod as true/false and use it as a global variable (similar to preventing recursive triggers).

 

Here is another challenge I have with testMethods (I can overcome it, but it is painful).

 

In a controller, I cycle through contacts that meet a certain criterion - I have 40K contacts in the database.


TestMethod now will fail because of too many SOQL queries. I was just creating "Test Contacts" before with isTest = true and filtering out the 40K contacts. Now either I have delete the 40K contacts (which I can't do in test method) or write some other hack code "if testmethod then return only the contacts I added during the testMethod". 

 

Message Edited by GoForceGo on 05-11-2009 09:54 AM