[SalesForce] Schedulable calling a batchable: doable? testable

I have a process that needs to run once daily. It will first need to make a series of callouts to get information (sms email address) to update contacts, and it will then need to send emails using the updated contacts. The callouts may exceed 10, so I have designed a Batchable class that can find all the correct records, make the callouts in batches, and update the records. From the finish() method I want to invoke the method to send the emails.

Here's the rough outline:

global class Reminders_Schedulable implements Schedulable {

    global void execute() {
        // OMITTED: do a little setup work 

        // Run the batchable to make callouts and update contact's SMS email addresses
        Database.executeBatch(new SMSGatewayUtil_Batchable(), SMSGatewayUtil.MAX_BATCH_SIZE);
    }

    global static void SendReminders() {
        // send the actual reminders using the updated contacts
    }
}

global class SMSGatewayUtil_Batchable 
             implements Database.Batchable<SObject>, Database.AllowsCallouts {

    global Database.QueryLocator start(Database.BatchableContext bc) {
         system.debug('&&&& SMSGatewayUtil_Batchable.start');
         return Database.getQueryLocator(...);
    }

    global void execute(Database.BatchableContext bc, list<Contact> contacts) {
         system.debug('&&&& SMSGatewayUtil_Batchable.execute');
         SMSGatewayUtil.UpdateContactsSMSEmail(contacts);
         update contacts;
    }

    global void finish(Database.BatchableContext bc) {
         system.debug('&&&& SMSGatewayUtil_Batchable.finish');
         Reminders_Schedulable.SendReminders();
    }

}

So the scheduleable class's execute() method runs the batchable, which updates makes callouts and updates contacts; finally the batchable's finish() method calls a static method
to send the emails, using the updated contacts. Is there are reason why the schedulable's execute() method shouldn't be able to call the batchable()? I don't think there is, but thought I'd check.

The bigger question – is this testable as is? I have a test method that calls system.schedule to run the schedulable (in between startTest and stopTest). The execute() method is called, and I see the SMSGatewayUtil_Batchable constructor invocation on the executeBatch() line in the log, but the batchable doesn't appear to run – the debug statements in the batchable class do not even appear in the log. There's also no error messages.

Update: As I failed to mention originally, my test class is setting up a mock interface for the callout. The question that I have is that the Database.Batchable is being constructed but not invoked – as shown in my outline above, each method in the Batchable has a debug statement, and none of them are showing up in the debug log – not even the start() method.

Best Answer

Yes, you can call batchable classes from schedulable classes. This is a very common use case.

As for testing you'll want to test them separately. Test methods for my schedulable classes that invoke a batchable class just verify that the batchable class is queued after the scheduled class has executed. I then have a separate test methods for my batch class that is queued for the test directly rather than through the schedulable class.

The reason behind this is the Test.stopTest() method only clears out queues (future, batch, scheduled) once. So you can have multiple future and batch calls run at that point, but if those cause any further queuing of processes they aren't run. Currently there isn't a way to flush the queues twice, just need to test them separately, which personnally I feel is a better and clearer testing scheme since it separates the various roles of each class better.

Related Topic