I am struggling to successfully test some code that is essentially the same as the code here Apex test class for schedulable class that checks for 5 batches running already. I can get the System.Schedule
to be called but the scheduled code appears to never be run in the test.
The Apex Scheduler documentation (referenced in Does Test.stopTest() cause ALL waiting asynchronous functions to fire?) says:
Use the Test methods startTest and stopTest around the System.schedule method to ensure it finishes before continuing your test. All asynchronous calls made after the startTest method are collected by the system. When stopTest is executed, all asynchronous processes are run synchronously.
I created the separate test case shown below and can reproduce the problem there; test1
passes but test2
and test3
fail. (Using API 30.)
What am I missing here?
Test:
@isTest
private class ScheduleBatchTest {
private enum TestCase {
DatabaseExecuteBatch,
ScheduleBatch,
Schedule
}
@isTest
static void test1() {
execute(TestCase.DatabaseExecuteBatch);
}
@isTest
static void test2() {
execute(TestCase.ScheduleBatch);
}
@isTest
static void test3() {
execute(TestCase.Schedule);
}
private static void execute(TestCase tc) {
Account[] accounts = new Account[] {
new Account(Name = 'Acme'),
new Account(Name = 'Nike')
};
insert accounts;
Test.startTest();
if (tc == TestCase.DatabaseExecuteBatch) {
Database.executeBatch(new BatchableExecutorTestBatchable());
} else if (tc == TestCase.ScheduleBatch) {
System.scheduleBatch(new BatchableExecutorTestBatchable(),
'my description', 5, 100);
} else if (tc == TestCase.Schedule) {
System.schedule('my job', '0 0 13 * * ?', new ScheduledBatchable());
}
Test.stopTest();
Account[] actuals = [select Site from Account where Id in :accounts];
System.assertEquals(accounts.size(), actuals.size());
for (Account actual : actuals) {
System.assertEquals('executed', actual.Site);
}
}
private class ScheduledBatchable implements Schedulable {
public void execute(SchedulableContext sc) {
Database.executeBatch(new BatchableExecutorTestBatchable(), 100);
}
}
}
Test batchable:
public class BatchableExecutorTestBatchable implements
Database.Batchable<SObject>, Database.Stateful {
public Database.QueryLocator start(Database.BatchableContext bc) {
Database.QueryLocator ql = Database.getQueryLocator([
select Name from Account order by Name
]);
insert new Account(Name = 'start');
return ql;
}
public void execute(Database.BatchableContext bc, List<SObject> scope) {
for (SObject sob : scope) {
Account a = (Account) sob;
a.Site = 'executed';
}
update scope;
insert new Account(Name = 'execute');
}
public void finish(Database.BatchableContext bc) {
insert new Account(Name = 'finish');
}
}
PS
Replacing the asserts after the Test.stopTest() with this:
System.debug(tc + ' ' + [select ApexClassId, JobType, Status from AsyncApexJob]);
resulted in this debug output showing two flavours of not completed AsyncApexJob:
- DatabaseExecuteBatch (AsyncApexJob:{JobType=BatchApex, Status=Completed, Id=7075000000acpcpAAA, ApexClassId=01p500000005gPGAAY}, AsyncApexJob:{JobType=BatchApexWorker, Status=Completed, Id=7075000000acpcqAAA, ApexClassId=01p500000005gPGAAY})
- ScheduleBatch (AsyncApexJob:{JobType=BatchApex, Status=Queued, Id=7075000000acpcsAAA, ApexClassId=01p500000005gPGAAY})
- Schedule (AsyncApexJob:{JobType=ScheduledApex, Status=Queued, Id=7075000000acpcuAAA, ApexClassId=01p500000005wM3AAI})
System.Schedulable
as a top-level public class to see if that helps (it didn't)