8

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?

6
  • 1
    don't you need to requery CronTrigger after stopTest()?
    – cropredy
    Mar 23, 2016 at 19:58
  • Yes and I had, but mistakenly excluded it from my post for some reason. Issue isn't resolved by the requery however.
    – smohyee
    Mar 23, 2016 at 20:35
  • 1
    This looks like a bug. I'm going to see if I can verify this.
    – sfdcfox
    Mar 24, 2016 at 2:57
  • 1
    gist.github.com/brianmfear/7564980aac77942fb535 proves that the code is running, but the TimesTriggered value doesn't increment. I don't know if this is documented behavior, but I'll see what I can find.
    – sfdcfox
    Mar 24, 2016 at 3:12
  • 1
    @smohyee My interpretation is that Test.stopTest() forces the scheduler execute() method to get called, but it does not update the underlying CronTrigger record. Because the CronTrigger is not actually triggering the execute - the testing infrastructure is.
    – codeulike
    Nov 22, 2016 at 12:30

5 Answers 5

4

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.

3
  • cropredy, I understand your point. The SF documentation's sample test class for a basic scheduled class works the way you describe: they don't count 'timestriggered', they check changes resulting from the actual scheduled logic.
    – smohyee
    Mar 23, 2016 at 20:42
  • BUT, that doesn't really address the issue I'm raising here, which is that timetriggered is NOT incrementing, suggesting that the scheduled job is NOT firing at Test.stopTest(). Understanding what's going on is the goal of my post (as opposed to actually testing functionality)
    – smohyee
    Mar 23, 2016 at 20:43
  • others may chime in here but there are sufficient "adaptations/accommodations" in the way SFDC testmethods work with async stuff versus production that I just "moved on" and stopped worrying about it :-)
    – cropredy
    Mar 23, 2016 at 20:47
3

As of 16-August-2018 this appears to be an issue. Note the following apex test method.

@isTest static void myTestMethod() {

    System.Test.StartTest();
        String jobId = System.schedule(xxx, xxx, xxx);
    System.Test.StopTest();

    //Assertion failed. Expected "Finished" actual "Waiting"
    System.assertEquals('Finished', [select State from CronTrigger where Id = :jobId ][0].State);
}
2

You need to query for the CronTrigger record for the scheduled job again after test.stopTest() to get the up-to-date value for TimesTriggered. Same as any other object you use a SOQL query to get.

4
  • 1
    Sorry, I had removed that line from the example I pasted for some reason. I did indeed requery crontrigger after test.stopTest(), and timestriggered was still 0. Updating code in post to reflect that.
    – smohyee
    Mar 23, 2016 at 20:34
  • (I threw a system.debug('Cron - post: ' + ct); line after requerying and confirmed that ct.TimesTriggered didn't increment)
    – smohyee
    Mar 23, 2016 at 20:37
  • So much for the easy answer. I'm afraid that I'm stumped.
    – Derek F
    Mar 23, 2016 at 20:42
  • One other difference from sample code provided by SF documentation is that I have SeeAllData=True set, which is necessary for another test method in that same test class. Can't imagine it's relevant, but then IDK...
    – smohyee
    Mar 23, 2016 at 20:45
0

same happened to me and I had to create a schedule class that class a batch class, and then create the respective test classes

Test.start();
Database.executeBatch(new UserActivationJob_JCS());
Test.stopTest();
//assert the logic

Test.startTest();
String jobId = System.schedule('TestScheduledApex', '0 0 0 3 9 ? 2022', new UserActivationJobSchedule_JCS());
Test.stopTest();

//assert the batch will run
-3

you have to remove the @isTest from your method; otherwise, the changes inside your batch will not be committed. Of course, when the test ends, the rollback is done automatically:

static void test_schedulable(){ ... }

you should not use testmethod in this case as well.

1
  • 1
    The user is trying to actually run a test which confirms behavior. This suggestion would make that impossible.
    – Adrian Larson
    May 14, 2021 at 15:52

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .