Take the 2-minute tour ×
Salesforce Stack Exchange is a question and answer site for Salesforce administrators, implementation experts, developers and anybody in-between. It's 100% free, no registration required.

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);  
share|improve this question
    
I'm having trouble getting a siilar test case to work - would you mind having a quick look at salesforce.stackexchange.com/questions/36806/… and commenting if anything occurs to you? Thanks. –  Keith C May 23 at 14:16

3 Answers 3

up vote 6 down vote accepted

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.

share|improve this answer
    
Wonderful answer! Worked perfectly. –  Jomtung Mar 3 at 18:48
    
simple and sleek :) –  MnZ Jun 19 at 8:31

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...
share|improve this answer
    
I would be in no way offended if you edited my answer to include this. –  ca_peterson Mar 1 at 7:25

I wonder if it is necessary to check for 5 running jobs, considering that your code uses the scheduler. Here's why:

If there are 5 jobs running and the time for a scheduled job arrives, then the platform should just wait for the next resource (job slot) to open up.

While you can write unit tests for this, since it is a situation that won't happen, I wonder if it is worth the time.

Having said that, perhaps you want to write something to check if the system has 100 jobs scheduled... via the CronTrigger (with a lookup to CronJobDetail) object.

Having said THAT, if you're trying to check for 5 running jobs and you want to be sure you don't do an executeBatch on top of those, that would be understandable. But for the record, I think that checking for running jobs and then scheduling a job on top of them (in your production code) is unnecessary and may cause your app to cease functioning if it sees five running jobs and just doesn't schedule another one. Your chain will break.

share|improve this answer
    
The queue for processing scheduled jobs and the one for batch jobs are distinct and this is a situation that can occur in the wild. The platform throws an uncatchable AsyncException if you try and call Database.executeBatch and 5 jobs are running. Ideally it would queue this new job for execution later, but alas. Since the scheduled job processing happens independently it certainly is possible to have a scheduled job try and start a batch job, hitting that AsyncException. –  ca_peterson Mar 1 at 20:25

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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