function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
iRiteshiRitesh 

Error When only send a HttpCallout in batch request

hi 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.setMock(HttpCalloutMock.class, new MirrorMockTimelinePostImpl());
Test.startTest();
Test.setMock(HttpCalloutMock.class, new MirrorMockTimelinePostImpl());

insert feedList;
Test.stopTest();
}

 

and My trigger code is 

trigger PostFeedsToTimeLine on FeedItem (after insert) {

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

 

and My Batch class code is 

public class BatchPublishTimeLine implements Database.Batchable<sObject>{
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');
}
}

 

and 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;
}
}

 

and i am unable to figure it out how using only one callout its throwing an error ?? Please help

Salesforce Hidden FactsSalesforce Hidden Facts
You are doing everything correct .Only the dml statement you
have in your test meathod have to be out side your start test
stop test block .refer winter 13 release notes
for testing webservice callouts.

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

Hi Ritesh,

 

The way you built this code needs to be refactored a bit. You need to split your test class to test the Trigger Separately and the Batch Class separately. 

 

Just try to test the batch class that is doing the callout by passing the FeedItem list to test your Http & Batch. 

 

and as mentioned by Salesforce Hidden Facts, try inserting the test data (feeditems ) before the StartTest() and then set the mock inside the Test.StartTest() and then call your Batch Class. 

 

Also, I noticed that you are not implementing Database.AllowCallouts in your actual Batch Class. Please do that otherwise it will throw you an error.

 

Tip: to control when to execute triggers try using a static boolean variable. 

 

Thank you

Kartik

 

 

 

 

RIteshMRIteshM

ok i implemented Database.Allowscallouts .Is there any thing wrong if trigger calls the Batch apex ?? if i do nothing when test is running ok thats work but i think it should also work when trigger executes batch apex.

peregrinus4lifeperegrinus4life
Hi Ritesh,

There is nothing wrong if trigger calls Batch Class, but the problem is that you are inserting the record after you setMock and inside Test.startTest(),

"The rule with Callouts is you cannot do a DML Operation before any Callouts", its been like this since beginning. Just to allow users to get around this, Salesforce implemented a rule saying that in Test Class if you are testing callout , insert all your data before starting the test. So for your test to run insert the data before Test.startTest(). But since you have a trigger that fires on Insert - there is no way you can insert without firing batch/callout causing the same problem you've had. So control your trigger execution and write tests accordingly.

If you still have issues, let me know and I will send you some sample code.

iRiteshiRitesh

Thanks a lot for the help but see the code below.My Test code function is this

 

@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()));
insert feedList;
String xx='%@'+UserInfo.getName()+'%';
feedList = [Select Body,CreatedById From FeedItem Where ParentId = :UserInfo.getUserId()];
for(Integer i=0;i< feedList.size();i++){
System.debug('List is'+feedList);
System.debug('item is '+feedList.get(i) +' i is'+ i);
if( !(feedList.get(i).Body == null) && (!feedList.get(i).Body.contains('@'+UserInfo.getName())))
{feedList.remove(i);
i--;}
}
System.debug('size is '+feedList.size());
Integer i= 2;
if(feedList.size() >2)
while(feedList.size() >2)
feedList.remove(i);
System.debug('size is '+feedList);
Map<String,String> contentMap = new Map<String,String>{'Object' =>'FeedItem','Content' =>'Body'};
Test.startTest();
BatchPublishTimeLine time1 = new BatchPublishTimeLine(feedList,contentMap);
Database.executeBatch(time1,5);
Test.stopTest();
}

 

and my batch apex is same as above but  my createTimeline function is

public static void createTimeLine(List<sObject> objList,Map<String,String> contentMap){

if(Test.isRunningTest())
Test.setMock(HttpCalloutMock.class, new MirrorMockTimelinePostImpl());

String timelineRes = doApiCall('xyzzz','POST','https://www.googleapis.com/mirror/v1/timeline','xxxxxxxxx');

}

 

and doApiCall function is same as above but i changed trigger for some time to this

 

trigger PostFeedsToTimeLine on FeedItem (after insert) {

Map<String,String> contentMap = new Map<String,String>{'Object' =>'FeedItem','Content' =>'Body'};
if(Trigger.new.size() >= 5){
BatchPublishTimeLine publishBatch = new BatchPublishTimeLine(Trigger.new,contentMap);
// Database.executeBatch(publishBatch,5);
}
// else GMirrorUtil.createTimeLine(Trigger.new, contentMap);

}

 

but when i run my test case still i am getting the same error 

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

according to your suggestion it should not show this error .please give reply.

peregrinus4lifeperegrinus4life

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.