It isn’t super clear in the documentation (V36) what happens in a test method when a Batchable, Queueable, and Schedulable are involved within the Test.startTest()...Test.stoptest()
execution scope.
The system executes all asynchronous processes started in a test method synchronously after the Test.stopTest statement
So, I decided to do a simple experiment:
The class (acts as both a batchable, queueable, and schedulable)
public with sharing class FooBatchableQueueable implements Database.Batchable<Sobject>, Queueable { public Database.QueryLocator start(Database.BatchableContext bc) { System.debug(LoggingLevel.INFO,'Entered Batchable start()...'); return Database.getQueryLocator([select id from Group where DeveloperName = 'Foo']); } public void execute(Database.BatchableContext bc, List<Group> scope) { System.debug(LoggingLevel.INFO,'Entered Batchable execute()...'); System.enqueueJob(new FooBatchableQueueable()); System.debug(LoggingLevel.INFO,'within Batchable execute(), after enqueuing the job...'); } public void finish(Database.BatchableContext bc) { System.schedule('FooSchedulable','0 0 0 1 1 ?', new FooSchedulable()); System.debug(LoggingLevel.INFO,'within Batchable finish(), after scheduling'); } public void execute(QueueableContext qc) { System.debug(LoggingLevel.INFO,'reached Queueable execute()'); } public class FooSchedulable implements Schedulable { public void execute(SchedulableContext sc) { System.debug(LoggingLevel.INFO,'reached Schedulable execute()'); } } }
And the testmethod
@isTest private with sharing class FooBatchableQueueableTest { @isTest private static void testBatchableQueueable() { insert new Group(DeveloperName='Foo', name='Foo', type='Regular'); Test.startTest(); Database.executeBatch(new FooBatchableQueueable()); Test.stoptest(); // Async batchable should execute, then queueable, // then schedulable. Or do they? See debug log } }
And, what does happen?
- The batchable
start()
andexecute()
execute fine. - The
execute()
callsSystem.enqueueJob(..)
- The Queueable job starts, and its
execute()
method is invoked. See the debug Log - The batchable
finish()
method executes. It does aSystem.schedule()
on a new object. - The schedulable’s
execute
does not start.
Debug log
Entered Batchable start()…
Entered Batchable execute()…
within Batchable execute(), after enqueuing the job…
reached Queueable execute()
Entered Batchable finish()…
within Batchable finish(), after scheduling
Conclusion
- Both the batchable and the queueable, as async transactions are executed “synchronously” once the
Test.stopTest()
is reached in the testmethod. - You definitely cannot assume that the batch finish() will execute before the queueable
execute()
. - The constructor for the schedulable class will get invoked, but not its
execute()
method. You can see no debug log from within the schedulable’sexecute()
. - You will need to explicitly test the schedulable by mocking the environment prior to its scheduling and then invoking in a separate testmethod.