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
Mike JackMike Jack 

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

Hi All,

I am integrating Salesforce with Box using HTTP Callouts and Box API Reference. I have written a Controller for a Visualforce page which displays Box Widget in the detail page. I am following this link and able to cover only 36% and getting an exception - System.CalloutException: You have uncommitted work pending. Please commit or rollback before calling out  and test is failed.

Here is the Controller:
global class BoxAccountFolderWidgetController {
    
    global string WidgetLink {set;
                              get
                              {
                                  return WLink;
                              }
                             }
    
    public string AccntId = ApexPages.currentPage().getParameters().get('id'); 
    public Account ac = [select id, name, RecordTypeId, Box_Folder_ID__c from Account where id=:AccntId];
    public string BoxFolderId = ac.Box_Folder_ID__c;
    
    public BoxRefreshToken__c BoxCS = [select id, RefreshToken__c, ClientId__c, ClientSecret__c from BoxRefreshToken__c limit 1];
    
    public string ClientId = BoxCS.ClientId__c;
    public string ClientSecret = BoxCS.ClientSecret__c;
    public string RefreshToken = BoxCS.RefreshToken__c;
    public string endPointURL='';
    public string AccessToken='';
    public string SharedLink='';
    public string WLink='';
    String Response ='';
    
    global BoxAccountFolderWidgetController(ApexPages.StandardController controller) 
    {
        
    }
    
    global void WidgetMethod()
    {
        system.debug('RefreshToken '+RefreshToken);
        //Getting Access Token and Refresh Token
        HttpRequest req = new HttpRequest();
        endpointURL = 'https://api.box.com/oauth2/token';
        req.setEndpoint(endPointURL);
        req.setMethod('POST');
        req.setHeader('Content-Type','application/x-www-form-urlencoded');
        req.setbody('grant_type=refresh_token'+
                    '&refresh_token='+RefreshToken+
                    '&client_id='+ClientId+
                    '&client_secret='+ClientSecret);
        req.setHeader('Accept','application/json');
        Http http = new Http();
        
        HTTPResponse res = http.send(req);
        Response=res.getBody();
        system.debug('Response '+Response);
        Integer statusCode=res.getStatusCode();
        
        // Parse JSON response to get AccessToken and RefreshToken values.
        JSONParser parser = JSON.createParser(Response);
        while(parser.nextToken() != null) 
        {
            if((parser.getCurrentToken() == JSONToken.FIELD_NAME))
            {
                String fieldName = parser.getText();
                parser.nextToken();
                if(fieldName == 'access_token') {
                    AccessToken = parser.getText();
                }
                if(fieldName == 'refresh_token') 
                {
                    RefreshToken = parser.getText();
                }
            }
        }
        system.debug('AccessToken '+AccessToken);
        system.debug('RefreshToken '+RefreshToken);
        
        //Creating Shared Link for the Folder
        HttpRequest req2 = new HttpRequest();
        endpointURL = 'https://api.box.com/2.0/folders/'+BoxFolderId;
        req2.setEndpoint(endpointURL);
        req2.setMethod('PUT');
        req2.setHeader('Content-Type','application/x-www-form-urlencoded');
        req2.setHeader('Accept','application/json');
        req2.setHeader('Authorization', 'Bearer ' +AccessToken);
        req2.setbody('{"shared_link": {"access": "open"}}');
        
        Http http2 = new Http();
        
        HTTPResponse res2 = http2.send(req2);
        String Response2=res2.getBody();
        system.debug('Response2 '+Response2);
        Integer statusCode2=res.getStatusCode();
        
        // Parse JSON response to get Shared Link values.
        JSONParser parser2 = JSON.createParser(Response2);
        while(parser2.nextToken() != null) 
        {
            if((parser2.getCurrentToken() == JSONToken.FIELD_NAME))
            {
                String fieldName = parser2.getText();
                parser2.nextToken();
                if(fieldName == 'shared_link')
                {
                    system.debug('Enter shared_link ');
                    while(parser2.nextToken() != null) 
                    {
                        if((parser2.getCurrentToken() == JSONToken.FIELD_NAME))
                        {
                            String fieldName1 = parser2.getText();
                            parser2.nextToken();
                            if(fieldName1 == 'url')
                            {
                                system.debug('Enter url '+parser2.getText());
                                SharedLink = parser2.getText();
                            }
                        }
                    }
                }
            }
        }
        
        String s = SharedLink.substringAfter('s/');
        system.debug('s '+s);
        WLink = 'https://app.box.com/embed_widget/s/'+s+'?view=list&sort=name&direction=ASC&theme=blue';
        
        if(RefreshToken != null)
        {
            BoxCS.RefreshToken__c = RefreshToken ;
            update BoxCS;
        }
    }  
}

Here is the VF Page:
<apex:page standardController="Account" extensions="BoxAccountFolderWidgetController" action="{!WidgetMethod}" >    
    
    <iframe src="{!WidgetLink}" width="1000" height="400" frameborder="0" ></iframe>
    
</apex:page>

Test Class for above Controller:
@isTest
public class Test_BoxAccountFolderWidgetController {
    
    public static testmethod void test1()
    {
        Account ac = New Account (name ='TestAccount', Box_Folder_Id__c = '8644998269');
        insert ac;
        
        
        Apexpages.StandardController sss = new Apexpages.StandardController(ac);
        apexpages.currentpage().getparameters().put('id' , ac.id);
        
        
        BoxRefreshToken__c BoxCS = new BoxRefreshToken__c ( RefreshToken__c = 'AJtikYVgIUhEsvgPPnfPMLBvrhCtB3RCQFGon8TdWdxboJ1wt4XLUAyglMuqeVqF',
                                                           ClientId__c ='pgevke65b3i6ujpqbfmhpeh0b2tx7o6r',
                                                           ClientSecret__c = 'mwKeNUq8VARg6wIwzfLI84h47SLnlmnp');
        insert BoxCS;
        
        
        
        Map<string,string> Headers = new Map<string,string>();
        Headers.put('Content-Type', 'application/x-www-form-urlencoded');
        
        SingleRequestMock fakeResp = new SingleRequestMock(200,
                                                                  'Complete',
                                                                  '{"access_token":"YUBoKdFFSoosuj2xurAa2vnXqm6pUypB","expires_in":4084,"restricted_to":[],"refresh_token":"dWEDG768lQhtbIECGuYT5i9DCoq7Sd61ousFUD6Mro9FP2oxDmbU6arGdo8lXGan","token_type":"bearer"}',
                                                                  Headers);
        
        Test.startTest();
        Map<string,string> Headers1 = new Map<string,string>();
        Headers1.put('Content-Type', 'application/x-www-form-urlencoded');
        Headers1.put('Authorization', 'Bearer kCEBaUaNAfqvMenkBflDgqF22pBd5Gka');
        String Body1= '{"shared_link": {"access": "open"}}';
        
        SingleRequestMock fakeResp1 = new SingleRequestMock(200,
                                                                   'Complete',
                                                                   Body1,
                                                                   Headers1);
        
        Map<String, HttpCalloutMock> endpoint2TestResp = new Map<String,HttpCalloutMock>();
        endpoint2TestResp.put('https://api.box.com/oauth2/token',fakeResp);
        endpoint2TestResp.put('https://api.box.com/2.0/folders',fakeResp1);
        
        HttpCalloutMock multiCalloutMock =
            new MultiRequestMock(endpoint2TestResp);
        
        Test.setMock(HttpCalloutMock.class, multiCalloutMock);
        BoxAccountFolderWidgetController abc = new BoxAccountFolderWidgetController(sss);
        abc.WidgetMethod(); 
        Test.stopTest();
        //System.assertEquals(/*check for expected results here...*/);
    }
}
Any Help is appreciated.

Best Regards,
MJ
 
VineetKumarVineetKumar
Performing a DML operation on a record and a callout cannot be made in the same transaction
You will have to split your logic into two parts:
  • 1st method that makes the callout.
  • 2nd method that does the update.
You need to make 2 different calls to the methods i.e., to say that something like method 2 will be called onComplete of 1st method.
Mike JackMike Jack
Hello VineetKumar,
Appreciate your response.

I changed the Controller like this below by splitting into two methods as you said. But still encountering the same error when execiting the Test Class.
 
global class BoxAccountFolderWidgetController {
    
    global string WidgetLink {set;
                              get
                              {
                                  return WLink;
                              }
                             }
    
    public string AccntId = ApexPages.currentPage().getParameters().get('id'); 
    public Account ac = [select id, name, RecordTypeId, Box_Folder_ID__c from Account where id=:AccntId];
    public string BoxFolderId = ac.Box_Folder_ID__c;
    
    public BoxRefreshToken__c BoxCS = [select id, RefreshToken__c, ClientId__c, ClientSecret__c from BoxRefreshToken__c limit 1];
    
    public string ClientId = BoxCS.ClientId__c;
    public string ClientSecret = BoxCS.ClientSecret__c;
    public string RefreshToken = BoxCS.RefreshToken__c;
    public string endPointURL='';
    public string AccessToken='';
    public string SharedLink='';
    public string WLink='';
    String Response ='';
    
    global BoxAccountFolderWidgetController(ApexPages.StandardController controller) 
    {
        
    }
    
    global void WidgetMethod()
    {
        system.debug('RefreshToken '+RefreshToken);
        //Getting Access Token and Refresh Token
        HttpRequest req = new HttpRequest();
        endpointURL = 'https://api.box.com/oauth2/token';
        req.setEndpoint(endPointURL);
        req.setMethod('POST');
        req.setHeader('Content-Type','application/x-www-form-urlencoded');
        req.setbody('grant_type=refresh_token'+
                    '&refresh_token='+RefreshToken+
                    '&client_id='+ClientId+
                    '&client_secret='+ClientSecret);
        req.setHeader('Accept','application/json');
        Http http = new Http();
        
        HTTPResponse res = http.send(req);
        Response=res.getBody();
        system.debug('Response '+Response);
        Integer statusCode=res.getStatusCode();
        
        // Parse JSON response to get AccessToken and RefreshToken values.
        JSONParser parser = JSON.createParser(Response);
        while(parser.nextToken() != null) 
        {
            if((parser.getCurrentToken() == JSONToken.FIELD_NAME))
            {
                String fieldName = parser.getText();
                parser.nextToken();
                if(fieldName == 'access_token') {
                    AccessToken = parser.getText();
                }
                if(fieldName == 'refresh_token') 
                {
                    RefreshToken = parser.getText();
                }
            }
        }
        system.debug('AccessToken '+AccessToken);
        system.debug('RefreshToken '+RefreshToken);
        
        //Creating Shared Link for the Folder
        HttpRequest req2 = new HttpRequest();
        endpointURL = 'https://api.box.com/2.0/folders/'+BoxFolderId;
        req2.setEndpoint(endpointURL);
        req2.setMethod('PUT');
        req2.setHeader('Content-Type','application/x-www-form-urlencoded');
        req2.setHeader('Accept','application/json');
        req2.setHeader('Authorization', 'Bearer ' +AccessToken);
        req2.setbody('{"shared_link": {"access": "open"}}');
        
        Http http2 = new Http();
        
        HTTPResponse res2 = http2.send(req2);
        String Response2=res2.getBody();
        system.debug('Response2 '+Response2);
        Integer statusCode2=res.getStatusCode();
        
        // Parse JSON response to get Shared Link values.
        JSONParser parser2 = JSON.createParser(Response2);
        while(parser2.nextToken() != null) 
        {
            if((parser2.getCurrentToken() == JSONToken.FIELD_NAME))
            {
                String fieldName = parser2.getText();
                parser2.nextToken();
                if(fieldName == 'shared_link')
                {
                    system.debug('Enter shared_link ');
                    while(parser2.nextToken() != null) 
                    {
                        if((parser2.getCurrentToken() == JSONToken.FIELD_NAME))
                        {
                            String fieldName1 = parser2.getText();
                            parser2.nextToken();
                            if(fieldName1 == 'url')
                            {
                                system.debug('Enter url '+parser2.getText());
                                SharedLink = parser2.getText();
                            }
                        }
                    }
                }
            }
        }
        
        String s = SharedLink.substringAfter('s/');
        system.debug('s '+s);
        WLink = 'https://app.box.com/embed_widget/s/'+s+'?view=list&sort=name&direction=ASC&theme=blue';
        
        UpdateRefreshToken();
    }  
    
    Public void UpdateRefreshToken()
    {
        if(RefreshToken != null)
        {
            BoxCS.RefreshToken__c = RefreshToken ;
            update BoxCS;
        }
    }
}

 
VineetKumarVineetKumar
You need to make two operation in different transactions
Performing a DML operation on a record and a callout cannot be made in the same transaction.
Said that, you cannot make make the call to second method as you are doing in line 120.

Since you are making a callout using a test class, which you will not be able to make, and you will have to mock the response.
https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_restful_http_testing_httpcalloutmock.htm

You can set some test value for the RefreshToken and make the code coverage.
Vamsi MopideviVamsi Mopidevi
You need to do perform alll DML operations before making the callout. It wont allow us to perform DML operations after callouts.