Here is how you resolve this - you basically have to call the start(), execute(), finish()
manually rather than rely on them all being executed after test.stoptest();
insert new Account(name = '00test'); // create test data here
Test.startTest();
Test.setMock(HttpCalloutMock.class, myMultiMockObject); // setup of multiMockObject omitted for clarity
Database.BatchableContext bc;
MyBatchableClass bClass = new MyBatchableClass();
// we execute the start() and prepare results for execute()
// in my use case, start() does the callout;
// thus the testmethod mocks the results of the callout (assumed here to be accounts)
// setup of custom Iterable and Iterator not shown
MyIterable itrbl = (MyIterable)bclass.start(); //start() returns an iterable, in my case, a custom iterable. Note the casting
MyIterator itrator = (MyIterator) itrbl.iterator(); // continue simulation of start() by constructing the iterator
List<Account> aScopeList = new List<Account> (); // create scope for execute() by iterating against the result of the mocked callout
while (itrator.hasNext()) {
aScopeList.add(itrator.next());
// Now invoke execute() w/ Account list built via mocked callout invoked by start()
bClass.execute(bc,aScopeList);
// Finally, invoke finish()
bClass.finish(bc);
Test.stopTest(); // SFDC executes the async task here
System.assert(...) //verify results
this approach accomplishes the following:
- It will test code coverage as start(), execute(), and finish() are all invoked as if SFDC were invoking them by Database.executeBatch().
- It avoids the System.CalloutException: You have uncommitted work pending. Please commit or rollback before calling out
- It allows you to test against the returned data from the mocked callout(s) thus isolating your testmethod to predictable responses.
- It tests your custom iterator, if you are using one
- It only requires a bit more setup to individually invoke start(), execute(), and finish() plus manual construction of what execute() would get if database.executeBatch() were called
I blogged about this here - http://cropredysfdc.com/2014/06/18/batchable-class-test-data-callouts-how-to-test/ which in turn was based on an earlier solution from Getting CalloutException when testing Batchable class with Callouts and HttpCalloutMock