[SalesForce] Testing scheduled apex: CronTrigger does NOT fire after Test.stopTest()

EDIT for clarification of title:

CronTrigger is probably actually firing at some point in the testing process – what is wierd is that CronTrigger.TimesTriggered is NOT incrementing after Test.stopTest()

Any asynchronous calls made within the Test.startTest() and Test.stopTest() methods of a test class should execute upon hitting Test.stopTest().

So, presumably, if I schedule a class using System.schedule(), the CronTrigger object representing that scheduled job should fire immediately upon Test.stopTest(), regardless of CronTrigger.NextFireTime's value.

Here is my schedulable class:

global class Schedulable_Nightly implements Schedulable {

    //PREDEFINED_SCHEDULE = "Every day at midnight"
    public static String PREDEFINED_SCHEDULE = '0 0 0 * * ?';

   global void execute(SchedulableContext SC) {
      Database.executeBatch(new ContractTimelineBatch());
   }
}

And here is my test method, pretty much lifted from SF documentation:

private class TestContractTimelineBatch {           

    @isTest static void test_schedulable(){

       Test.startTest();

          // Schedule the test job
          String jobId = System.schedule('testBasicScheduledApex',
          Schedulable_Nightly.PREDEFINED_SCHEDULE, 
             new Schedulable_Nightly());

          // Get the information from the CronTrigger API object
          CronTrigger ct = [SELECT Id, CronExpression, CronJobDetailId, CronJobDetail.Name, TimesTriggered, PreviousFireTime, NextFireTime, State
             FROM CronTrigger WHERE id = :jobId];

          // Verify the job has not run
          System.assertEquals(0, ct.TimesTriggered);

       Test.stopTest();

       ct = [SELECT Id, CronJobDetailID, TimesTriggered, PreviousFireTime, NextFireTime, State
         FROM CronTrigger WHERE id = :jobid];

       //DEBUG STATEMENT SHOWS THAT ct.TimesTriggered IS STILL 0
       system.debug('Cron - post: ' + ct);

       // Verify that the job has now run   
       System.assertEquals(1, ct.TimesTriggered);

    }

}

This final assertEquals fails because ct.TimesTriggered stays at 0, both before and after Test.stopTest

What gives?

Best Answer

You could take a different approach to your test method

Instead of asserting whether the schedulable class was, in fact, executed, you should be asserting whether the schedulable's execute() method actually did anything and what you expected. Testing that SFDC can schedule/execute a schedulable per its CronTrigger proves nothing - as SFDC is guaranteed to execute your code at or around the time specified in the CronTrigger.

In your case, whether the batch job processed the mocked sobjects is the interesting thing to test.

Related Topic