11

I am sending only a single HTTP Request in Batch but still facing error System.CalloutException: You have uncommitted work pending. Please commit or rollback before calling out

My test code is

@isTest(SeeAllData=true)
static void test1(){
    MirrorTestUtil.setupSettings();
    User u = [Select Authorize__c From User Where Id = :UserInfo.getUserId()];
    u.Authorize__c = true;
    update u;
    System.debug('u is '+u);

    List<FeedItem> feedList= new List<FeedItem>();
    for (Integer i = 0; i<5;i++)
        feedList.add( new FeedItem(Body='@'+UserInfo.getName()+'Hello World'+i,ParentId = UserInfo.getUserId()));
    Test.startTest();
    Test.setMock(HttpCalloutMock.class, new MirrorMockTimelinePostImpl());

    insert feedList;
    Test.stopTest();
}

My trigger code is

trigger PostFeedsToTimeLine on FeedItem (after insert) {

    BatchPublishTimeLine publishBatch = new BatchPublishTimeLine(Trigger.new,contentMap);
    Database.executeBatch(publishBatch,5);
}

My Batch class code is

public class BatchPublishTimeLine implements Database.Batchable<sObject>,Database.AllowsCallouts{
    sObjectIterable iterable;
    Map<Id,User> userMap;
    Map<String,String> contentmap;

    public BatchPublishTimeLine(List<sObject> objectList,Map<String,String> contentmap){
        iterable = new sObjectIterable(objectList);
        this.userMap = new Map<Id,User>([Select Id,Name From User WHERE Authorize__c = true]);
        this.contentmap = contentmap;
    }
    public Iterable<sObject> start(Database.BatchableContext BC){
        return iterable;
    }
    public void execute(Database.BatchableContext BC, List<sObject> scope){
        GMirrorUtil.createTimeLine(scope, contentMap);
    }

    public void finish(Database.BatchableContext BC){
        System.debug('Job Has been Finished');
    }
}

My createTimeLine function code is

public static void createTimeLine(List<sObject> objList,Map<String,String> contentMap){    
    String timelineRes = doApiCall('xyzzz','POST','https://www.googleapis.com/mirror/v1/timeline','xxxxxxxxx');
}

doApiCall code is

public static String doAPICall(String postBody, String method, String endPoint, String accessToken){

    HttpRequest req = new HttpRequest();
    Http http = new Http();
    HttpResponse res;

    req.setEndpoint(endPoint);
    req.setMethod(method);
    req.setHeader('Content-Type','application/json');

    if(method == 'POST' || method == 'PUT')
        req.setBody(postBody);
    req.setHeader('Authorization','Bearer ' + accessToken);
    res = http.send(req);
    String result = res.getBody();
    System.debug('status code is '+res.getStatus());
    System.debug('result is'+res.getBody());
    return result;
}

and the Mock class code response is

@isTest
global class MirrorMockTimelinePostImpl implements HTTPCalloutMock {
    global HTTPResponse respond(HTTPRequest req) {
        // Optionally, only send a mock response for a specific endpoint
        // and method.
        System.assertEquals('https://www.googleapis.com/mirror/v1/timeline', req.getEndpoint());
        System.assertEquals('POST', req.getMethod());
        System.assert(req.getHeader('Authorization').startsWith('Bearer'));

        // Create a fake response
        HttpResponse res = new HttpResponse();
        res.setHeader('Content-Type', 'application/json');
        res.setBody('{"kind":"mirror#timelineItem", "id":"mockid", "created":"2013-07-31T12:07:34.882Z", "updated":"2013-07-31T12:07:34.882Z", "etag":"\\"ZECOuWdXUAqVdpmYErDm2-91GmY/NVMWuR8LJyCKttsmne9R4K8n7YI\\"", "text": "New Lead: OauthCheck EarlyAm, Google, Inc., (234) 567-8900"}');
        res.setStatusCode(200);
        return res;
    }
}

I am unable to figure out how using only one callout is throwing an error!

  • I'm having the same issue except it only happens when I try to deploy to production. In FC there is no error. You ever figure out exactly what was going on? – NSjonas Dec 10 '15 at 1:47
12

Run it with the following line commented out and see if it works..

feedList.add( new FeedItem(Body='@'+UserInfo.getName()+'Hello World'+i,ParentId = UserInfo.getUserId()));
//Test.setMock(HttpCalloutMock.class, new MirrorMockTimelinePostImpl());
Test.startTest();
Test.setMock(HttpCalloutMock.class, new MirrorMockTimelinePostImpl());

The Test.startTest statement must appear before the Test.setMock statement. Also, the calls to DML operations must not be part of the Test.startTest/Test.stopTest block.

http://boards.developerforce.com/t5/Apex-Code-Development/Testing-Webservice-Callouts-with-Winter-13-Test-setMock-fails/td-p/510427/page/3

  • unfortunately i tried this and it doesn't work. – user133421 Sep 26 '13 at 5:25
  • 2
    Ok. I think there is still a bug with the DML executing a Trigger and then doing a callout. Can you try commenting out the //insert feedList and run your test class. It worked for me. The other DMLs are working fine (UPDATE User) its just the ones that have Triggers defined are causing issue. Maybe there is still an issue if we have DML that fires triggers. – logontokartik Sep 27 '13 at 2:40
  • above comment resolve my problem :D – user133421 Oct 26 '13 at 5:32
  • 2
    commenting out the insert feedList defeats the whole purpose of this test, since the insert trigger calls the batch apex, which in turn makes the callout – willard Feb 5 '14 at 17:10
4

It looks like you may be updating a User record prior to the HTTP call (i.e. when the GlassUserApiSettings__c custom setting does not have a record for the current User being iterated over.

Also, you may find issues later on attempting to update a User record and a record of another type as part of a single transaction.

  • No i am not updating any user whuile testing one minute Updating writing the whole code – user133421 Sep 26 '13 at 2:41
  • Now see the full code and then reply if possible.stuck badly – user133421 Sep 26 '13 at 2:43
  • you can see i am only making a callout in batch class is this some thing wrong from salesforce side ?? – user133421 Sep 26 '13 at 2:44
4

One of the main reason of this exception is that you are doing some CRUD operation (create,read,update,delete) in your code in same context of running test, means you are doing some DML operation and then doing call out in single run.

Your DML code must be outside the Test.startTest(). You can try with this structure

public static void testmethod()
{
  doDML(); // do some update,insert etc.. outside
  Test.startTest()
  doAPiCalloutTest(); // Implemetnt MockTest and write API Call statements
  Test.stopTest();
}

One workaround can be, you skip the http.send(req) execution in main class by using Test.isRunningTest and provide fake response there by this check.

  • perhaps Test.setMock doesn't work in batch class .Please confirm and tell . i am also searching. – user133421 Sep 26 '13 at 7:48
  • @user133421 setMock works everywhere. It generally fails in batch if there are some DML happens before call out – Ashwani Sep 26 '13 at 7:52
  • I have same exception Can someone help me,See the link: salesforce.stackexchange.com/questions/41440/… – Ramesh S Jun 25 '14 at 5:50
3

Does your code even work outside of the test methods? You're not using Database.AllowsCallouts on your batch class definition, which is required if you want to make callouts from inside it.

Also, your batch class doesn't seem to actually leverage the mock response and looks like it's attempting to make a genuine call-out during the test.

While testing batch apex I often find it's easier to invoke the start() and execute() methods manually, this might help you as wlel.

  • Thanks for reply but still getting same error i changed it to public class BatchPublishTimeLine implements Database.Batchable<sObject>,Database.AllowsCallouts{ but still facing the same issue in code.Please help .need to resolve it . – user133421 Sep 26 '13 at 4:14
2

So as far as I can ascertain, mock callouts and batches don't prevent the uncommitted error IF the callout is contained within the batch you are testing...see here for a longer explanation:

Testing a combination of webservice callouts and inserts

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.