1

I have a batch class which in turn makes REST callouts(No future annotation). Now I want to write test class for same.

Currently what i tried -

-StaticResourceCalloutMock class in salesforce - Problem with this is as also mentioned over comments here that I can not make my test data before calling the batch as it will give Pending work error.Link - https://developer.salesforce.com/blogs/developer-relations/2012/10/testing-http-callouts-with-static-data-in-winter-13.html

-I removed the use of StaticResourceCalloutMock class and tried direct batch callout, but I can not call methods from test methods which in turn use Callouts.

-In my code, it self I tried setting the response that if it is Test.isRunningTest then give hardcoded response, but again i dont want hardoded response but need response based on test data I have created.

I feel totally stuck here. Anyone got any idea or pointer to help me out. Please let me know.

Thanks,

Kaushik

1 Answer 1

2

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:

  1. It will test code coverage as start(), execute(), and finish() are all invoked as if SFDC were invoking them by Database.executeBatch().
  2. It avoids the System.CalloutException: You have uncommitted work pending. Please commit or rollback before calling out
  3. It allows you to test against the returned data from the mocked callout(s) thus isolating your testmethod to predictable responses.
  4. It tests your custom iterator, if you are using one
  5. 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

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .