[SalesForce] Getting CalloutException when testing Batchable class with Callouts and HttpCalloutMock

I'm trying to write test code for a Batchable class that AllowsCallouts. I've written a lot of other test code that uses HttpCalloutMock classes, and planned to do so for this class as well.

And, per best practices, I'm setting up my test data before calling Test.startTest(). Even so, I get a CalloutException:

System.CalloutException: You have uncommitted work pending. Please commit or rollback before calling out

This somewhat makes sense, because my start() method passes a SOQL query to Database.getQueryLocator() and my execute() method makes the web callouts. I'm guessing that when my test method calls test.stopTest(), both start() and execute() run in the same context. In regular batch operation, they happen in separate contexts so there isn't any uncommitted work.

I intend to work around this by calling the methods directly, possibly even calling start() before Test.startTest(), and then using the returned Database.QueryLocator to get the list of records I'll pass into execute().

I don't like the fact that I'm not testing the batch size used by the static method that kicks off the batch with Database.executeBatch(), so I'll probably have that use a constant that I can reference from my test code to ensure it's using the same batch size.

Salesforce kind of addresses this in the "Asynchronous Apex and Mock Callouts" section of the Summer '13 Release Notes, but they don't make reference to Batchable classes with Callouts. They only talk about making callouts and testing Batchable classes in the same test method.

So, my question is whether there is a better way to write this test. I'm also throwing this out so others searching the site can learn about possible workarounds.

Best Answer

I know this is a 'hack' but you may be able to call your batchable class methods inside your testmethod without running in a Database.executeBatch(...); method call. I have done this for a customer who needed higher test coverage; their batch apex was simple and didn't require the batchable context to be set.

TestCode:-

    MyBatchableClass mbc = new MyBatchableClass();

        List<MySObject__c> scope = {list of previously inserted objects};
        mbc.execute(null, scope);
        Test.startTest();
        WebServiceMockImpl mock = new WebServiceMockImpl();

        Test.setMock(WebServiceMock.class, mock);
        //Webservice call out in here
        mbc.finish(null);

        Test.stopTest();
Related Topic