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
Lorenzo PeregoLorenzo Perego 

Test for Trigger calling @future (callout=True)

Hello to everyone, I have this problem that I have to solve as soon as possible.

I have an Account trigger after insert that calls an external http request.

It works all in sandbox, the call comes to the web server and does what it needs to do.

When I'm going to deploy it into production, the test (very basic) returns error:
System.CalloutException: You have uncommitted work pending. Please commit or rollback before calling out.

Here is my trigger after insert
 
trigger Account_Trigger on Account (after insert,after update) {
	if (Trigger.isInsert){
		for(Account a : Trigger.new ){
			String acc_json = JSON.serialize(a);
	    	Future.insert_cliente(acc_json);             
    	        }                       
	}
	else if (Trigger.IsUpdate) {
		for(Account a : Trigger.new ){
			String acc_json = JSON.serialize(a);
	    	Future.update_cliente(acc_json);             
    	        }  
	}
}
Here is my Future Class
 
public class Future {
    
    public static void update_cliente(String obj) {
    	String end_point = 'http://xxxxxxxxxx/saleforce/update_cliente/';
    	Put(obj, end_point);
    }
    
    public static void insert_cliente(String obj) {
    	String end_point = 'http://xxxxxxxxxx/saleforce/insert_cliente/';
    	Put(obj, end_point);
    }
    
    @future(callout=true)
    public static void Put(String obj, String end_point)
    { 
        Http http = new Http();
        HttpRequest request = new HttpRequest();
        request.setHeader('Content-Type', 'application/json;charset=UTF-8');
        request.setEndpoint(end_point);
        request.setMethod('PUT');
        Blob headerValue = Blob.valueOf('xxxxx'+ ':' + 'xxxxx');
        String authorizationHeader = 'Basic ' + EncodingUtil.base64Encode(headerValue);
        request.setHeader('Authorization', authorizationHeader);
        request.setBody(obj);
        HttpResponse response = http.send(request);
        // If the request is successful, parse the JSON response.
        if (response.getStatusCode() == 200) {
                System.debug(response.getStatusCode());
        }
        else{
            System.debug(response.getStatusCode());    
        }  
    }
}

Here is my FutureMock to use in Test:
 
@isTest
global class FutureMock implements HttpCalloutMock
{
    global HttpResponse respond(HttpRequest request)
    {
        HttpResponse res = new HttpResponse();
        res.setBody( '{"my":"value"}');
        res.setStatusCode(200);
        return res;
    }
}

Here is my Test for Account that returns me error:
 
@isTest
private class Account_TriggerTest
{
	 public static testMethod void testWithExistingDec()
	{
		Test.setMock(HttpCalloutMock.class, new FutureMock());
		profile p= [select id from profile limit 1];
		User u=new user(username='aa@test.it', lastname='aaa', Email='aa@test.it', 
                                           TimeZoneSidKey='Europe/Rome', LocaleSidKey='it_IT', 
		                           EmailEncodingKey='ISO-8859-1', 
                                           LanguageLocaleKey='it',CommunityNickname='test',Alias='aaa', 
                                           Profileid=p.id)  ; 
		insert u;
		Account acc=new Account (name='ABCD', Ownerid=u.id);
		insert acc;
		System.assertEquals(acc.Ownerid,u.id); 
		
	}
}

Can someone help me?
What am I doing wrong?

 
Asif Ali MAsif Ali M
Not sure why it is working in Sandbox but I see some problem with the Account trigger. Please see my suggection below and let me know if it works.

Account_Trigger
trigger Account_Trigger on Account (after insert,after update) {
if (Trigger.isInsert) {
        Future.insert_cliente(Trigger.new);
    } else if (Trigger.IsUpdate) {
        Future.update_cliente(Trigger.new);

    }
}

Future Class:
public class Future {
    
    public static void update_cliente(List<Account> accounts) {
    	String end_point = 'http://xxxxxxxxxx/saleforce/update_cliente/';
            for(Account a : accounts ){
               String obj = JSON.serialize(a);
               Put(obj, end_point);
           }
    }
    
    public static void insert_cliente(List<Account> accounts) {
    	String end_point = 'http://xxxxxxxxxx/saleforce/insert_cliente/';
    	 for(Account a : accounts ){
               String obj = JSON.serialize(a);
               Put(obj, end_point);
           }
    }
   
}

If the external end point is in your control then I suggest you to make changes to it to reach multiple objects in single request and change the Future class like below.

 
public class Future {
    
    public static void update_cliente(List<Account> accounts) {
    	String end_point = 'http://xxxxxxxxxx/saleforce/update_cliente/';
        String obj = JSON.serialize(accounts);
        Put(obj, end_point);     
    }
    
    public static void insert_cliente(List<Account> accounts) {
    	String end_point = 'http://xxxxxxxxxx/saleforce/insert_cliente/';
        String obj = JSON.serialize(accounts);
        Put(obj, end_point);
    }
   
}