4

I'm trying to mock Database.BatchableContext to make my unit tests more robust and to eliminate excess usage of Test.isRunningTest() running in production code. Few hacks are available like Json.deserialize('{"jobId":""}', Database.BatchableContextImpl.class) but feels bad.

My batch class implementation is boring and ordinary:

public class LongWalksOnTheBatch implements Database.Batchable {

    public Iterable start(context) {...}

    public void execute(context, scopes) {...}

    public void finish(context) {...}

}

And most of the time, these techniques are sufficient:

  1. Using Test.startTest() and Test.stopTest() to run 1 iteration:

    //arrange
    Database.Batchable batch = new LongWalksOnTheBatch();
    
    //act
    Test.startTest();
    Database.executeBatch(batch, 1);
    Test.stopTest();
    
    //assert
    System.assert(sideEffects);
    
  2. Manually calling the execute() method to observe more complex behaviours:

    //arrange
    Database.Batchable batch = new LongWalksOnTheBatch();
    
    //act
    batch.execute(null, new List<SObject>());
    batch.execute(null, new List<SObject>());
    batch.execute(null, new List<SObject>());
    
    //assert
    System.assert(sideEffects);
    

But this gets a bit crap when that Database.BatchableContext is actually needed. For example to abort the job. But trying to getJobId() will result in a big fat NullPointerException. So what to do?

4

This code compiles indicating Database.BatchableContext is an interface:

public class MockBatchableContext implements Database.BatchableContext {
    private Id jobId;
    private Id childJobId;
    public MockBatchableContext(Id jobId, Id childJobId) {
        this.jobId = jobId;
        this.childJobId = childJobId;
    }
    public Id getJobId() {
        return jobId;
    }
    public Id getChildJobId() {
        return childJobId;
    }     
}

so in your technique 2) you can avoid the NPE by passing in an instance of such a class. But you would still have to guard any System.abortJob calls by checking Test.isRunningTest().

  • This is great, Keith!!! Undocumented but totally a goer :D – bigassforce Aug 16 '15 at 21:55
  • The exception thrown by System.abortJob() can be caught in the test, as a proxy to signal the abort was attempted :-) System.StringException: Job does not exist or is already aborted – bigassforce Aug 17 '15 at 0:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.