Cool, we can chain batches now! But we can’t test them. Do we?

I just used the new feature that allows me to call a SecondBatchable class from the finish() method of the FirstBatchable class.

global with sharing class FirstBatchable implements Database.Batchable<SObject>, Database.Stateful {

   global void finish(Database.BatchableContext context) {

      SecondBatchable batch = new SecondBatchable();
      Database.executeBatch(batch, 5); // It even fails with value 1
   }
   ...
}

This all works like a charm. But… All my tests of this class

@isTest
private static void testBatch() {
    ...
    Test.startTest();

    FirstBatchable batch = new FirstBatchable();
    Database.executeBatch(batch, 200); // It even fails with value 1

    Test.stopTest();

    ...
} 

fail with the error:

System.UnexpectedException: No more than one executeBatch can be
called from within a testmethod. Please make sure the iterable
returned from your start method matches the batch size, resulting in
one executeBatch invocation.

I guess the problem is as we chain TWO batches the Test.startTest and Test.stopTest bring the batchSize down to 1 but 1 + 1 != 1.

Any suggestions?!

Answer

I’ve encountered this issue when testing a scheduled class that launches a batch job. When the code hits Test.startTest() any queued jobs get called which in turns queued a batch job. What starting happening is that at the termination of the test method there was a second clear out of the queued jobs which caused a multi-batch execution issue.

In your case I’m guessing that your finish method gets run when you hit Test.startTest() but you don’t get an exception until your test method concludes and the second purge happens.

To work around this I aborted the queued second job as a last statement in the test. For example:

@isTest
private static void testBatch() {
    ...
    Test.startTest();
    FirstBatchable batch = new FirstBatchable();
    Database.executeBatch(batch, 200);
    Test.stopTest(); // your batch executes here

    // abort the pending batch job
    AsyncApexJob pending = [select id from AsyncApexJob where apexClass.Name = 'SecondBatchable'];
    system.abortJob(pending.id);
} 

If that doesn’t work you’ll want to just through a test switch in there. From a testing perspective you should be able to break up the tests into segments that test each batch individually. You’ll just need to do the work of the first batch in the test setup when testing the second.

Attribution
Source : Link , Question Author : Robert Sösemann , Answer Author : Ralph Callaway

Leave a Comment