You need to sign in to do that
Don't have an account?
jwolf
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?
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
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());
}
}
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?
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.
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".