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?

1 Answer 1

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().

2
  • This is great, Keith!!! Undocumented but totally a goer :D Aug 16, 2015 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 Aug 17, 2015 at 0:12

You must log in to answer this question.

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