9

I've setup some chain execution batch classes. For this setup we decided to chain the batch classes through schedulable classes. We thought it might be a good idea to include a check for 5 batch jobs running so we didn't hit the concurrent batch processing limit. Here's the method we put into our schedulable classes:

global class Schedule_Batch implements Schedulable {
  global void execute(SchedulableContext sc) {            
    MyBatch b = new MyBatch();

    //check if there are 5 active batch jobs
    if([Select count() FROM AsyncApexJob WHERE JobType='BatchApex' AND(Status = 'Processing' OR Status = 'Preparing')]<5){
        Database.executebatch(b);
    }else{
        //schedule this same class again in 30 minutes
        Schedule_Batch bagain = new Schedule_Batch();
        Datetime dt = Datetime.now() + (0.024305); // i.e. 30 mins
        String timeForScheduler = dt.format('s m H d M \'?\' yyyy');
        Id schedId = system.Schedule('CFP_TaskBatch_Retry'+timeForScheduler,timeForScheduler,bagain);
    }
  } 
}   

This was taken from the developer forums - http://help.salesforce.com/apex/HTViewSolution?id=000182449&language=en_US.

The problem is unit testing. I've not been able to get to the else part of the if statement that finds at least five concurrent jobs running. Would someone please help me to test out 5 concurrent batch jobs running so I can have full coverage for the schedulable class? Here's the relevant part of my test class that I'm trying so far:

MyBatch b = new MyBatch();        
Id BatchprocessId = Database.executeBatch(b);

MyBatch b2 = new MyBatch();      
Id BatchprocessId2 = Database.executeBatch(b2);

MyBatch b3 = new MyBatch();       
Id BatchprocessId3 = Database.executeBatch(b3);

MyBatch b4 = new MyBatch();            
Id BatchprocessId4 = Database.executeBatch(b4);

MyBatch b5 = new MyBatch();      
Id BatchprocessId5 = Database.executeBatch(b5);        

Schedule_Batch q = new Schedule_Batch();
SchedulableContext scq;
q.execute(scq);  
9

You're going to have to fake it. What I do in situations like this is create a property with a getter and setter, but where the setter can only be used in tests.

As an example, setup a new property:

public static Integer runningJobCount{
    get{
        if(runningJobCount == null)
            return [Select count() FROM AsyncApexJob WHERE JobType='BatchApex' AND(Status = 'Processing' OR Status = 'Preparing')];
        else
            return runningJobCount;
    }
    set{
        System.assert(Test.isRunningTest(),'This property may only be set in tests');
        runningJobCount = value;
    }
}

Then change this line:

    if([Select count() FROM AsyncApexJob WHERE JobType='BatchApex' AND(Status = 'Processing' OR Status = 'Preparing')]<5){

to

    if(runningJobCount <5){

Then in your test you can set *Schedule_Batch.runningJobCount = 5;* in the test you want to test this scenario.

  • Wonderful answer! Worked perfectly. – Jomtung Mar 3 '14 at 18:48
  • simple and sleek :) – MnZ Jun 19 '14 at 8:31
3

One thing I would add to the answer above - I prefer to store the max batches variable (5 in the answer above) in a custom setting for two reasons:

  1. You can then set it differently for different tests
  2. 5 might be too high at some point - I have seen several packages/implementations that require a batch to be running most of the time - so in that circumstance, you would probably want your check to be able to leave at at least one batch queue available...having it in a setting makes it easier to adjust on the fly...
  • I would be in no way offended if you edited my answer to include this. – ca_peterson Mar 1 '14 at 7:25

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.