[SalesForce] Test Class Error for web service callouts “System.CalloutException: You have uncommitted work pending. Please commit or rollback before calling out”

Actually there are many answers for this error "System.CalloutException: You have uncommitted work pending. Please commit or rollback before calling out", I tried all possible solutions, but couldn't solve my problem so I am posting this question.

I have a class where I'm doing web service callouts, but I'm invoking that class through a trigger. Everything is working fine and I'm happy, but it is in a sandbox that I'm trying to move to production. I'm facing a problem in the test class, so kindly help me on this.

In all the solutions I saw it says you have to complete the DML operation and then call Callouts or they also said to use @future to make the call asynchronous.

Here are my class, trigger and test class:

My class:

public class DN_send_sms{
    @Future(callout=true)
    public static void DN_SMS(string mobile,string name,string Slabname,string psAmountvalue,
        string status,string duedate) {
        // list<account> acc=[SELECT Id,Phone FROM Account where id=:conId];

        String Username ='ab@xyx.com';
        String Password = '321';
        String TempID;
        String MobileNo=mobile;
        String SenderID = 'ABC';
        String F1;
        String F2;
        String F3;
        String F4;

        if(status == 'Paid')
        {
            TempID = '20000' ;
            F1 = name + ' ';
            F2 = Slabname;
            F3 = psAmountvalue;
        }
        else if(status=='Not - Paid')
        {
            TempID = '21873' ;
            F1 = name;
            F2 = Slabname;
            F3 = duedate;
        }

        String postData = 'username=' + Username + '&pass=' + Password 
            + '&dest_mobileno=' + MobileNo +'&senderid=' + SenderID + '&tempid=' 
            + TempID +'&F1=' + F1 + '&F2=' + F2+ '&F3=' + F3 + '&F4=' + F4;

        HttpRequest req = new HttpRequest();
        req.setEndpoint('http://123.56.01.01/blank/sms/user/urlsmstemp.php?'+postdata);
        req.setMethod('GET');
        system.debug(req);
        Http h = new Http();
        HttpResponse res = h.send(req);

        system.debug(res);
        //return res;
    }
}

My Trigger to call class

trigger DN_Generated on Debit_Note__c (after insert, after update) {

    for (Debit_Note__c DN : trigger.new){
        if(Trigger.IsInsert){
            DN_send_sms.DN_SMS(DN.Account_Phone__c,DN.Account_Name__c,Dn.Payment_Schedule_Name__c,
                string.valueof(Dn.Payment_Schedule_Value__c),DN.Debit_Note_Status__c,
                string.valueof(Dn.Due_Date__c));
        }

        // user u=[SELECT Alias,Id,MobilePhone,Name,Phone,ProfileId FROM User WHERE Id =: '0059000000101C6'];
        If(Trigger.isUpdate){

            if(trigger.oldmap.get(Dn.id).Debit_Note_Status__c!=DN.Debit_Note_Status__c)
            {
                DN_send_sms.DN_SMS(DN.Account_Phone__c,DN.Account_Name__c,Dn.Payment_Schedule_Name__c,
                    string.valueof(Dn.Pending__c),DN.Debit_Note_Status__c,string.valueof(Dn.Due_Date__c));
            }

        }
    }

}

Test class for trigger and class

@isTest(SeeAllData=true)
public class test_DN_SMS_Generated{
    public static testmethod void test_DN_SMS(){
        //user ur = [select id,Name from user where id=:'0059000000101C6'];
        Account acc = new Account();
        acc.name = 'Acc1';
        acc.Status__c = 'new';
        acc.Custom_AN__c = 1;
        acc.Project_Interested__c = 'atrium';
        acc.Phone1__c = '8015825000';
        acc.Email__c = 'sam@gmail.com';
        insert acc;

        Potential__c pot = new Potential__c();
        pot.name = 'Sam';
        pot.Account__c = acc.id;

        insert pot;

        user u = [SELECT Alias,Id,MobilePhone,Name,Phone FROM User where Alias=:'Asing'];

        string mobile = acc.Phone1__c;
        string name = acc.name = 'Acc1';
        string status = 'Planned';
        string Uname = u.name;
        string Umobile = u.MobilePhone;
        string Dateon;

        //Test.startTest();
        sitevistsendsms.sitevistsendsms1(mobile,name,status,dateon,Uname,Umobile);
        String Username = 'abc@xyz.com';
        String Password = '321';
        String TempID = '20000' ;
        String MobileNo = mobile;
        String F1 = name;
        String SenderID = 'UNI';
        String F2 = status;
        String F3 = umobile;
        string F4;
        String postData = 'username=' + Username + '&pass=' + Password + '&dest_mobileno=' 
            + MobileNo +'&senderid=' + SenderID + '&tempid=' + TempID+'&F1=' + F1+ '&F2=' 
            + F2+ '&F3=' + F3;

        Debit_Note__c DN = new Debit_Note__c();
        DN.name = 'DN_test';
        DN.Account__c = acc.id;
        DN.Debit_Note_Status__c = 'Paid';
        Insert DN;

        Test.startTest();
        Test.setMock(HttpCalloutMock.class, new Stubby());
        HttpRequest req = new HttpRequest();
        req.setEndpoint('http://123.56.01.01/blank/sms/user/urlsmstemp.php?'+postdata);
        req.setMethod('GET');
        Http h = new Http();
        HttpResponse res = h.send(req);
        //system.debug(res);
        test.stopTest();    
    }
}

This is the error which I'm facing:

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

Stack Trace : Class.test_DN_SMS_Generated.test_DN_SMS: line 57, column
1

Best Answer

Looks like you have the right components in the test but in the wrong order?

You're using the Test.setMock() after the Insert DN; (and that insert has the side-effect of making the callout).

Read through http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_callouts_wsdl2apex_testing.htm carefully. It doesn't involve a trigger but principle is the same - configure the mock before actually using the webservice :)


Alternatively a quick & dirty job (won't get you whole code coverage though) is to wrap the critical part of the method in an if statement:

HttpResponse res;
if(!Test.isRunningTest()){
    res = h.send(req);
}
return res;
Related Topic