I'm trying test a critical part of an application that is in a batch process and makes callouts to a server.

    MyObject__c obj = new MyObject__c(name='test');
    insert obj;

    Test.setMock(WebServiceMock.class, new MyWebServiceMock());
    //run the batch process

When I attempt to "mock" the callout, I get hit with this bug:

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

According to the docs on testing callouts, this shouldn't happen.

Dug into it a bit more and found this article where someone opened a case. Salesforce apperently responded by saying to "use the isTestRunning() method to avoid the callout".

This is obviously not ideal, as It makes it very hard to do any useful assertions. The only work around I can think of is to overload the batch constructor and pass my test inputs in. I would really like to avoid this if possible.

Anyone got a solution?

  • Could it be that you're hitting this error in a deploy? As for some reason salesforce checks the "don't do DML before callouts"-limit once during a deploy, so if you have a completely separate test running before your callout test, and it's doing a DML. Then you could run into this problem, take a look at: salesforce.stackexchange.com/questions/11606/…
    – pjcarly
    Dec 9 '14 at 8:30
  • Doesn't seem to matter how I run the test. I've been running using mavens-mate synchronous test runner, but I can also run using the async test and I get the same results. Only running the single test so order shouldn't matter.
    – NSjonas
    Dec 9 '14 at 9:08
  • 1
    Somehow you're doing a DML statement before the callout, perhaps a Database.setSavepoint(); which is often overlooked, but also consumes a DML.
    – pjcarly
    Dec 9 '14 at 12:18
  • Ahhh, your right. My function equivalent to MyBatchProcess.createBatch(obj); does preform some updates on OBJ. I completely overlooked that aspect. Any explanation on why it works in the real world, but not in a unit test? I guess I need to refactor these into two different functions if I want to test.
    – NSjonas
    Dec 9 '14 at 16:59
  • Also, if you submit an answer, I will give you the check
    – NSjonas
    Dec 9 '14 at 16:59

I have never gotten Batch and Callouts to work in tests - see here for more detail

My approach these days is to put the all the code from the batch in a regular class that operates as the Batch Handling Class with its own Start, Execute and Finish. That way, I can call the code with callouts directly in my tests and it will work fine. The batch uses that class, but then I can use Test.IsRunningTest() to avoid calling those methods in the test context. Not perfect by any means, but it is the easiest way to get batches, callouts and tests to play nicely...

  • Show how do you test different outputs from the callout inside of isTestRunning()? For a simplified example, what if my callout returns a status code and I do different things depending on its code (0,1,2,3 etc). How do you test each of those logical branches?
    – NSjonas
    Dec 9 '14 at 17:51
  • ohhh i think i got it.... The class could have a method that handles the status code returned by the callout. The callout itself would be made in the batch process with isTestRunning() and then pass the result into the class handler method. That way, you can test the class independent of the callout.
    – NSjonas
    Dec 9 '14 at 17:54
  • I let the handling class do everything - I just pass the scope variable to the handling class if I am not running a test....if you have an instance of the handling class (rather than calling a static method), you can then set variables in the handling class and retrieve those variables when you return to the batch if you are keeping a track of each execution for the final piece etc... Dec 10 '14 at 0:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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