1

My POST Method (data for web token - is my custom object's fields):

public class Token{
public String accessToken{get;set;}    
}

public static String accessTokenBody(){  //our web token (data is in fields)
        Settings__c settings = [SELECT ConsumerKey__c, ClientSecret__c, Username__c, Password__c, SecurityToken__c
                                FROM Settings__c
                                WHERE Name = 'OurSettings'];  
        String consumerKey = settings.ConsumerKey__c;
        String consumerSecret = settings.ClientSecret__c;
        String username = settings.Username__c;
        String password = settings.Password__c + settings.SecurityToken__c;
        String request = 'grant_type=password&client_id=' + consumerKey +'&client_secret=' + consumerSecret +
                         '&username=' + username + '&password='+password;
        return request;
    }

public static String GenerateJSON(Type__c t){
//I will send and post it like a record in another org:
    Map<String, String> fieldMap = new Map<String, String>{
                   'Name' => t.Name,
                   'Desc__c' => t.Type_Description__c};    
    String serialized = JSON.serialize(fieldMap);         
    return serialized;
 }

public static HttpRequest httpRequest(String service){
    String requestBody = accessTokenBody();
    HttpRequest ourRequest = new HttpRequest();
    ourRequest.setBody(requestBody);
    ourRequest.setMethod(service);
    ourRequest.setEndpoint('https://p21.lightning.force.com/services/oauth2/token');
    return ourRequest;
}

public static HttpRequest finalHttpRequest(String token, String method, String endpointUrl){
    HttpRequest finalRequest = new HttpRequest();
    finalRequest.setHeader('Authorization','Bearer ' + token);
    finalRequest.setHeader('Content-Type','application/json');
    finalRequest.setHeader('accept','application/json');
    finalRequest.setMethod(method);
    finalRequest.setEndpoint('https://p21.lightning.force.com/services/oauth2/token' + endpointUrl);
    return finalRequest;
}

public static HttpResponse postCallout(String positionId) {
    Http ourHttp = new Http();
    HttpRequest request = httpRequest('POST');
    //Error is here: 
    HttpResponse response = ourHttp.send(request);      
    Token objAuthenticationInfo = (Token)JSON.deserialize(response.getbody(), Token.class);

    if(objAuthenticationInfo.ACCESS_TOKEN != null){
        Type__c typ = [SELECT Id, Name FROM Type__c WHERE Id =: TypeID];
        HttpRequest finalRequest = finalHttpRequest(objAuthenticationInfo.ACCESS_TOKEN, 'POST', '');
        finalRequest.setBody(GenerateJSON(typ));
        HttpResponse finalResponse = ourHttp.send(finalRequest);
        if(finalResponse.getStatusCode() == 200) {
            System.debug('CREATED:  ' + finalResponse.getBody());
            return finalResponse;
        }else {
            return null;
        }
    }
    return null;
}

Mock:

@isTest
global class AnimalsHttpCalloutMock implements HttpCalloutMock {
    global HTTPResponse respond(HTTPRequest request) {
        HttpResponse response = new HttpResponse();
        response.setHeader('Content-Type', 'application/json');
        response.setBody('{"Name":"ttt"}');
        response.setStatusCode(200);
        return response; 
    }
}

I get an error System.CalloutException: You have uncommitted work pending. Please commit or rollback before calling out. I try to make the same web token in my test class, but I think it can't see it. Coverage is just on my getAccess(), httpRequest() and first 4 rows of postCallout():

@isTest
private class CalloutTest {


@isTest
static void testPostCallout() {      
    Test.setMock(HttpCalloutMock.class, new AnimalsHttpCalloutMock());

    public class Token{
        public String token{get;set;}    
    }

    Settings__c settings = new Settings__c(Name = 'OurSettings',
                                           ConsumerKey__c = '3Mv78JilhgTvnlkjlkjydGLbGpb8xcTB.2lhtT16LdER9QVx_AmoaOb6gN_3OEtHPlvBDzBYWieC189Cp',
                                           ClientSecret__c = '3453453453419024129',
                                           Username__c = '[email protected]',
                                           SecurityToken__c = '6qdfgcdY6ndyURd32J7Ls2VN',
                                           Password__c = 'mypassword1');
    insert settings;
    String consumerKey = settings.ConsumerKey__c;
    String consumerSecret = settings.ClientSecret__c;
    String username = settings.Username__c;
    String password = settings.Password__c + settings.SecurityToken__c;
    String request = 'grant_type=password&client_id=' + consumerKey +'&client_secret=' + consumerSecret +
                     '&username=' + username + '&password='+password;

    Http ourHttp = new Http();
    String requestBody = CalloutClass.accessTokenBody();
    System.debug('request: ' + request);
    System.debug('request2: ' + requestBody);
    HttpRequest ourRequest = new HttpRequest();
    ourRequest.setBody(request);
    ourRequest.setMethod('POST');
    ourRequest.setEndpoint('https://p21.salesforce.com/services/oauth2/token');
    Type__c typ = new Type__c(Name = 'ttt');
    insert typ;
    //System.CalloutException: You have uncommitted work pending. Please commit or rollback before calling out
    HttpResponse response = CalloutClass.postCallout(typ.Id);
    String contentType = response.getHeader('Content-Type');
    System.assert(contentType == 'application/json');
    String actualValue = response.getBody();
    System.debug(response.getBody());
    String expectedValue = '{"Name":"ttt"}';
    System.assertEquals(actualValue, expectedValue);
    System.assertEquals(200, response.getStatusCode());
}    
}

1 Answer 1

2

You are getting System.CalloutException: You have uncommitted work pending. Please commit or rollback before calling out error because A Web Service Callout may not occur after a DML statement within the same transaction.

Solution : To achieve the required action, the transaction must be separated into two parts so that the DML transaction is completed before the Web Service Callout occurs. So use Test.startTest and Test.stopTest to separate context.

 Test.startTest(); 
 Test.setMock(HttpCalloutMock.class, new AnimalsHttpCalloutMock());
 HttpResponse response = CalloutClass.postCallout(typ.Id); 
 Test.stopTest(); 
9
  • I changed it (replace iserting to the top). The same error.
    – user60684
    Oct 13, 2018 at 18:48
  • move these statements Type__c typ = new Type__c(Name = 'ttt'); insert typ; just after the insert settings statment. Oct 13, 2018 at 19:00
  • It is after insert settings already. Before Http ourHttp = new Http(); - same.
    – user60684
    Oct 13, 2018 at 19:18
  • 1
    Please check the updated answer. Oct 13, 2018 at 19:35
  • 1
    Thank you. Coverage is now bigger: on 5 first rows of postCallout() and last row return null;. Now Error is on String contentType = response.getHeader('Content-Type'); and asserts: System.NullPointerException: Attempt to de-reference a null object. I don't understand what it need. If I replace String and asserts into startTest and stopTest - same error.
    – user60684
    Oct 13, 2018 at 20:03

You must log in to answer this question.

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