1

Below class gives error "You have uncommitted work pending. Please commit or rollback after calling out".

//Call out from batch

 public void execute(Database.BatchableContext bc, List<Opportunity> scope) {       

        for(Opportunity o : scope){
            Http http = new Http();
            HttpRequest req = new HttpRequest();
            HttpResponse res = new HttpResponse();
            req.setEndpoint('https://posttestserver.com/post.php/?dump&dir=sandbox&' +o.name);
            req.setHeader('Content-Type','x-www-form-urlencoded');
            req.setHeader('Content-Length', '0'); 
            system.debug('here5');
            req.setMethod('GET');
            req.setTimeOut(1200);
            res = http.send(req);
            String str = res.toString();
}

//Test Method

@isTest
static void testRecordUpdate(){
    User u = createTestUser('System Administrator', 'Test', 'User');
    List<Opportunity> opp = createOppRecord('Open', 1, u);
    for(Opportunity o : opp){
        o.stageName = 'Closed Won';
    }
    Test.setMock(HttpCalloutMock.class, new CalloutMock());
    update opp; // this update makes callout
}

Trying to : When agent updates an opportunity, a callout is made and if response is successful, a record should be created in child object

Thanks.

3

1 Answer 1

3

A quick search may have found the answer, but it is really simple:

Wrap the mock in a test.startTest();

  test.startTest();
    Test.setMock(HttpCalloutMock.class, new CalloutMock());
    update opp; // this update makes callout
  test.stoptest();

the reason is just like in a non-test environment. You cannot make a callout after doing any dml. Since you are creating a User, you cannot do a callout after in the same transaction. the test.StartTest() provides a new context for the callout to be run in as well as a new set of governor limits....

NOTE

Not sure why you have a batch code there, but if you are executing a batch from a trigger (potentially a problem) you will have to:

Call the start, execute, finish methods separately as it is a know issues that you will get this error regardless of the test.startTest if the callout is from a batch

If that is the case, unfortunatly you will have to wrap that part of your trigger that calls the batch in a :

if(!test.isRunningTest())

and change your test method to:

test.startTest();
        Test.setMock(HttpCalloutMock.class, new CalloutMock());
        update opp; // this update makes callout
        //call your start method
        [batch].execute(null,New sObject[]{opp});
       //call your finish method
      test.stoptest();

You can also just wrap the callout in the batch to prevent it from running during a test except when a static property is set. that way your trigger is unaltered and the batch / test controls when the callout is made during a test method

i.e in the batch.

public static testCallout = false;
.....
if( (testCallout && test.isRunningTest()) || !test.isRunningTest())
    //make callout

then set the testCallout = true from your test class

this leaves your trigger unaltered

You can find many topics on this issue by searching for "You have uncommitted work pending batch test" on this site

6
  • Hi Eric, I tried wrapping update in test.starttest() and test.stoptest() but that didnt help. Why I have batch ? My requirement is: When an opportunity stage is closed, create a record in other custom object called "CalloutLogger". After record is created in this object, callout to webservice, if result is 200 ok, update CalloutLogger status to 'Complete' or Else 'Failed'. What I did ? After update trigger on opportunity inserts record in CalloutLogger. After Insert trigger on this object calls batch which makes a callout. Please share any improvements on this approach. Thanks
    – sf_user
    Commented Oct 18, 2015 at 21:46
  • 1
    @pbvsdt - Read my whole answer. It may have been updated since you first read it
    – Eric
    Commented Oct 18, 2015 at 21:48
  • Apex class is working as expected but gettting this problem in test method
    – sf_user
    Commented Oct 18, 2015 at 21:48
  • 1
    Yes you will. You need to stop your trigger from calling the batch during a test method.....and execute the start, execute, and finish methods separately from the test class
    – Eric
    Commented Oct 18, 2015 at 21:48
  • 1
    @pmvsdt - Glad to hear it. For now, this is the only way around it. I would encourage you to use the test.isrunningTest in the batch itself and use the static property to enter the callout during specific test. That way you preserve your codebase. *Please mark as accepted answer if it resolved your problem so the question can be found by others and does not keep surfacing months later as unanswered
    – Eric
    Commented Oct 18, 2015 at 22:07

You must log in to answer this question.

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