Take the 2-minute tour ×
Salesforce Stack Exchange is a question and answer site for Salesforce administrators, implementation experts, developers and anybody in-between. It's 100% free, no registration required.

I have a Controller (FooController) which calls an @future method (bar()) belonging to another class (FooServices).

Ideally in my test class for this Controller (FooControllerTest) I only want to test that the @future method has been called, I don't want to test its effects as that's FooServicesTest's job.

I know that I can use Test.startTest() and Test.stopTest() to see the effects of an @future method in a test class, but is there any way of identifying that a @future method has been called/queued up in a test class?

Since code is generally easier to understand than words, here is an example of what I would like to be able to do:

FooController

public class FooController
{
    public PageReference save()
    {
        // Do some operations...

        // Call my @future method
        FooServices.bar();

        return null;
    }
}

FooServices

public class FooServices
{
    @future
    public static void bar()
    {
        // Do some things in the future...
    }
}

FooControllerTest

@isTest
private class FooControllerTest
{
    @isTest
    private static void save_ValidData_CalloutQueued()
    {
        // Setup my test data...
        FooController controller = new FooController();
        controller.save();
        // Is this part possible? THE BELOW METHOD NAME IS ENTIRELY FICTIONAL
        System.assert(System.isFutureJobQueued('FooServices.bar'));
    }
    // OR...
    @isTest
    private static void save_ValidData_CalloutExecuted()
    {
        FooController controller = new FooController();
        Test.startTest();
        controller.save();
        Test.stopTest();
        // Yet another fictional method...
        System.assert(System.hasFutureJobExecuted('FooServices.bar'));
    }
}

share|improve this question

2 Answers 2

The method looks like this:

static public Boolean isFutureRunning(System.Type type, String methodName) {
    String namespacePrefix;
    String name;

    if (type.getName().contains('.')) {
        //managed (namespaceprefix.classname)
        namespacePrefix = type.getName().substringBefore('.');
        name = type.getName().substringAfter('.');
    } else {
        //unmanaged (classname)
        namespacePrefix = '';
        name = type.getName();
    }

    //find existing futures
    Integer existingFuturesCount = [
        SELECT COUNT()
        FROM AsyncApexJob
        WHERE ApexClass.NamespacePrefix = :namespacePrefix AND ApexClass.Name = :name
        AND MethodName = :methodName
        AND JobType = 'Future'
        AND Status IN ('Queued', 'Processing', 'Preparing')
    ];

    return existingFuturesCount != 0;
}
share|improve this answer
    
I'll give this a go when I'm back in the office, I'm assuming if I the Status clause I can pull out completed jobs? Is the AsyncApexJob table cleaned out between test methods or is it a 'system' style table where tests and your Org share the same data (like the RecordType table)? –  Alex Tennant Jun 16 at 18:13
2  
@AlexTennant Yes, if you use startTest and stopTest the status should reach Complete. As for visibility, both real and test apex jobs will be seen. But your test jobs don't exist beyond each test method. I'm really lazy and just count them before and after, then look for n + 1 when asserting. –  user320 Jun 16 at 19:48
    
@AlexTennant I've just confirmed that in Test context, Future jobs don't appear in the table. Only Schedulable and Batchable ones. At least it works in real code... heh –  user320 Jun 17 at 10:19
    
Oh well... back to the drawing board. It's a useful piece of code to keep around for non-test situations though. –  Alex Tennant Jun 17 at 10:21
    
For now I've put an Idea on the Idea Exchange: success.salesforce.com/ideaView?id=08730000000l6UYAAY –  Alex Tennant Jun 17 at 10:59

Yes!

I love this question, as it's one of my pet peeves. People don't test @future calling methods because they don't know it's possible.

Enter the magic of two key methods:

Test.StartTest();
Test.StopTest();

When you utilize these methods -- and you should! they do a couple of key things for you.

  1. they reset your governor limits so you can test if your actual code is going to hit Limits.
  2. Test.StopTest(), actually forces your @future methods to run right now and return before progressing to the next step.

so, in your test example, if you take your exact test and add those two methods thusly:

@isTest
private class FooControllerTest
{
    @isTest
    private static void save_ValidData_CalloutQueued()
    {
        // Setup my test data...
        FooController controller = new FooController();
        Test.StartTest();
        controller.save();
        Test.StopTest();
        // You can actually now assert that yes, it indeed was queued, 
        // and has run, and has returned data. Since this is a callout? 
        // look at the callout mocks for more info on returning mock 
        // data to assert on.
        System.assert(System.isFutureJobQueued('FooServices.bar'));
    }

}

With Regards to just identifying of a job has been queued:

Select ApexClassId, CompletedDate, CreatedById, CreatedDate, ExtendedStatus, Id,
JobItemsProcessed, JobType, LastProcessed, LastProcessedOffset, MethodName, 
NumberOfErrors, ParentJobId, Status, TotalJobItems from AsyncApexJob

This object, (and query above) should list all pending async apex jobs including enqueued @future methods. I expect (but have never tried) that you could build a where clause based on MethodName. ApexClassId would likely work as well, but would be ugly as the Id's may be hard to come by dynamically.

share|improve this answer
    
Thanks for this, but as I started in the question I know that I can use startTest and stopTest to ensure the @future runs and then check the results. What I want is a way of checking if it has been run/queued without checking for any specific results (as the results of the call will be tested by another class specific to that). –  Alex Tennant Jun 16 at 18:11
    
@AlexTennant - My bad. I've added a bit at the end with a soql query that should help you get near what you want. –  Kevin P Jun 16 at 20:42
    
As noted in the comments on @user320's answer, Future jobs are not added to the ApexAsyncJob table in test contexts. I've added an Idea on the Idea Exchange for now: success.salesforce.com/ideaView?id=08730000000l6UYAAY –  Alex Tennant Jun 17 at 11:02

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.