I have read several document regarding this ,including Testing webservice call out but still I cannot find the solution for my issue.
I am creating test method to test batch class that contain httpcallout method.When I run the test, the result is pass but when I went through the debug log, the code that called callout method such if(statusCode==200) is not covered and got exception like below:
System.CalloutException: You have uncommitted work pending. Please commit or rollback before calling out
Here the batch class
global class MyBatch implements Database.Batchable<sObject>, Schedulable, Database.Stateful,Database.AllowsCallouts {
String query='Select Id,Name,Status__c,UserId from Transaction__c where Status__c='\'Pending\'';
Map<String, MyObject> mapMyObject = new Map<String, MyObject>();
public MyBatch(){
//contructor contain initialization
}
global void execute(SchedulableContext sc) {}
global Database.QueryLocator start(Database.BatchableContext BC){
return Database.getQueryLocator(query);
}
global void execute(Database.BatchableContext BC, List<Transaction__c> transactions) {
for(Transaction__c t:transactions){
mapMyObject.put(t.UserId,new MyObject(Name='Bla bla');
}
system.debug('mapMyObject size '+mapMyObject.size());
for (MyObject o : mapMyObject.Values()){
//contain logic and call out
MyCallOut.getUserByEmail(email)
if(statusCode==200){
system.debug('Good job!')
}
//the rest of logic and call cout
}
}
}
Here the class that contain HttpCallout method that called by my batch class
global class MyCallOut {
public static string getUserByEmail(string email){
String returnStr='';
try{
String baseUrl = externalURL+'/2.0/admin/users/'+email;
system.debug('@getUserByEmail '+ baseUrl );
HTTPRequest request = new HTTPRequest();
request.setEndpoint(baseUrl);
String authorizationHeader = 'WRAP access_token=' + token;
request.setHeader('Authorization', authorizationHeader);
request.setHeader('Content-Type', 'application/xml');
request.setMethod('GET');
request.setTimeout(120000);
HTTP http = new HTTP();
HTTPResponse response;
response = http.send(request);
if(response.getStatusCode()>=400){
returnStr=response.getStatusCode() +' : '+ response.getStatus();
errorCode=response.getStatusCode();
errorMessage=returnStr;
}
else {
errorCode=response.getStatusCode();
returnStr=response.getBody();
}
system.debug('@returnStr '+ returnStr);
}
catch(Exception e){
errorCode=700;
errorMessage=e.getMessage();
errorMessage+= ' ::: inside getUserByEmail(string email)';
}
return returnStr;
}
public static string populateProfile(string str{
//HttpRequest bla bla
}
//more httpcall method
}
Here is the test method.I am using MultiStaticResourceCalloutMock,also please note that I already put Test.startTest() and Test.stopTest() properly as stated in reference link.
@isTest
public class MyBatchTest {
static{
URLConfig__c setting = new URLConfig__c();
setting.Name = 'Url';
setting.Value__c = 'https://api.bla.com';
insert setting;
URLConfig__c setting1 = new URLConfig__c();
setting1.Name = 'Key';
setting1.Value__c = 'ABC1234323';
insert setting1;
Id folderId=[Select Id from Folder Where Name='Token'].Id;
Blob bToken=Blob.valueOf('JYYTGHGUTY6567GYT8765gXDWSRET');
Document doc1 =new Document(Name='Token',Body =bToken,ContentType='text/plain',FolderId=folderId);
insert doc1;
List<Document> doc =[SELECT Id,Body,Name FROM Document WHERE Name='Token'];
System.assertEquals(1, doc.size());
User newUser=new User(alias = 'newuser', email='[email protected]',
Department='HR Management', username='[email protected]');
insert newUser;
System.AssertEquals(
database.countquery('SELECT COUNT()'
+' FROM User where Id = \''+newUser.Id+'\''),
1);
List<Transaction__c> trans = [SELECT Id,Name,UserId__c FROM Transaction__c where UserId__c =: newUser.Id];
system.debug('@test '+trans.size());
}
static testMethod void testBatch(){
MultiStaticResourceCalloutMock multimock = new MultiStaticResourceCalloutMock();
multimock.setStaticResource('https://api.bla.com/2.0/admin/users/[email protected]', 'UserProfile_Test');
multimock.setStatusCode(200);
multimock.setHeader('Content-Type', 'application/json');
Test.startTest();
Test.setMock(HttpCalloutMock.class, multimock);
MyBatch c = new MyBatch();
c.query='SELECT Id,Name,UserId__c,Status__c '+
+' FROM Transaction__c WHERE Status__c IN (\'Pending\') LIMIT 1';
Database.executeBatch(c,1);
Test.stopTest();
}
}
Thanks for your idea.