[SalesForce] Test class for http callouts

I need to cover my class code. I have http callouts in it. Any lead how to do that?
I've searched in the internet and found that httpcalloutmock help to test the http's request.

Any example please?!

My method callout is like this:

@future(callout=true)
public static void GetIdObject(set<string> listObjIds){
    mapObj = DAL.getMapObjectsByIds(listObjIds);
    List <Object__c> listObj = new List<Object__c>();
   // if(!system.Test.isRunningTest()){
     system.debug('*****************mapObjAvaya**************'+mapObj);
    for(Object__c Obj : mapObj.values()){
       if(Obj.Id !=null ){
        callWSGetIdObject(Obj);
        listObj.add(Obj); //listObj
        }     
    }
    update listObj;  //listObj
    // }  

}

   public static void callWSGetIdObject(Object__c Obj ){
    PAD.myLog('Calling the web service callWSGetIdObject');
    String agent;
    String BDValues; 
    String UserId = UserInfo.getUserId();
    HttpRequest req = new HttpRequest();
    HTTPResponse res = new HTTPResponse();              

    User user= [select Id, CRC__c, Alias, Type_Interne_Externe__c from User where Id=:UserId ];

   // if(!system.Test.isRunningTest()){

    PAD.myLog('##### Map Object ID WS Nice Bridge ______'+mapObj);

    agent = user.Alias;
    BDValues ='S-'+Obj.Id;
    PAD.MyLog('USER CRC '+ user.CRC__c);
        if (user.CRC__c=='CRC'){
           //isReqValid= true;

           String url = Label.url+ Label.CodeSwitch+
           + '&Agent=' + EncodingUtil.urlEncode(agent,'UTF-8')
           + '&BDNames=' + EncodingUtil.urlEncode(Label.BDNames,'UTF-8')
           + '&BDValues=' + EncodingUtil.urlEncode(BDValues,'UTF-8')
           + Label.Overwrite;
            PAD.myLog(url);

            // Add the endpoint to the request
            req.setEndpoint(url);
            req.setMethod('GET');
            Http http = new Http();

            try {
                if(!system.Test.isRunningTest()){
                res = http.send(req);
                }
                PAD.myLog('##### CALLOUT RESULT _____ '+res);                   
                PAD.myLog('##### CALLOUT RESPONSE _____ '+res.getBody());
                } 

                catch(System.CalloutException e) {
                    PAD.myLog('????Error in HttpRequest Callout' + res.toString());
                    PAD.myLog('###Exception :'+e.getTypeName()+' :' +e.getMessage());    
                }
            // create the xml doc that will contain the results of the Callout operation
            Dom.Document doc = new Dom.Document();

            try{

                XmlStreamReader reader = res.getXmlStreamReader();
                system.debug('#### XML READER ________ '+reader);
                doc.load(res.getbody());

                Dom.XMLNode root = doc.getRootElement();
                PAD.myLog('ROOT    '+root); 
                // Get the first child element of the root        
                Dom.XMLNode data= root.getChildElements()[0];
                // Get the second chile element of the root => Result element
                Dom.XMLNode result= root.getChildElements()[1];
                PAD.myLog('RESULT    '+RESULT); 
                // Get the childs of 'Result' Element         
                for(Dom.XMLNode child :root.getChildElements()){ 

                    if(child.getName() == 'Result'){
                        Obj.Status__c = child.getChildElements()[0].getText();
                        Obj.Message__c = child.getChildElements()[1].getText();
                        Obj.Status_Ex__c = child.getChildElements()[2].getText();
                        PAD.myLog('STATUS '+child.getChildElements()[0].getText());  
                        PAD.myLog('MESSAGE '+child.getChildElements()[1].getText());         
                        PAD.myLog('STATUS EXTENDED '+child.getChildElements()[2].getText());                
                        } 
                    }
                }
            // catching exception while invalid XML
            catch (System.XMLException e) {  
                    //isReqValid = false;
                    //LstObjToUpdate.add(Obj);
                    PAD.myLog(e.getMessage());

                  }
        }

        //}
        //system.debug('### ISREQVALID     '+isReqValid);
        /*if (isReqValid && !system.Test.isRunningTest())
            checkWsSuccessGetIdObject(res, Obj);*/
    }

My httpresponsegenerator Mock

     @isTest 
     global class MockHttpResponseGenerator implements HttpCalloutMock {

// Implement this interface method
global HTTPResponse respond(HTTPRequest req) {
    // Optionally, only send a mock response for a specific endpoint
    // and method.



    System.assertEquals('URL'
                        + '&Agent=Momo'
                        + '&BDNames=LDE_Name'
                        + '&BDValues=LDE_Value'
                        + '&Overwrite=-1', req.getEndpoint());

    System.assertEquals('GET', req.getMethod());
    // Create a fake response
    HttpResponse res = new HttpResponse();
    res.setHeader('Content-Type', 'text/xml');
    String body='<Output xmlns=\"nice.uniform://\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\"><Data i:nil="true"/><Result xmlns:a=\"http://nice"><a:ResultCode>RESULTCODE</a:ResultCode><a:ResultMessage>No open call exists</a:ResultMessage><a:ResultCodeEx>CLS_SE_NO_OPEN_CALL</a:ResultCodeEx></Result><Exception i:nil="true" xmlns:a="http://schemas.datacontract.org/2004/07/System"/></Output>';
    res.setBody(body);

    res.setStatusCode(200);
    Dom.Document doc = new Dom.Document();
    doc.load(body);
    return res;
}
}

My UnitTest:

 static testMethod void myUnitTestIdObj() {
        Profile p = [select id from profile where Name='Administrateur système' limit 1];
    User CurrentUser = TestCommon.createUser('trinity', '@test.sfd.test', 'MyTestLastName2','MyTestFirstName2',p.Id,'CRC');

     System.runAs(CurrentUser ) 
    {
        Product2 prod = new Product2(family = 'AAF', name = 'test', FMES__c = 0, Isactive = true);
          insert prod;

          Account acc = new Account(NClientLDE__c = '456', name = 'test', origine__c = 'Epsydre');
          insert acc;

          Object__c Obj = new Object__c(produit__c = prod.id, DatedeObject__c= Date.today(), client__c = acc.id, Motifresiliation__c = label.Venteforcee);
          insert Obj;

          Obj.produit__c = prod.id;
          update Obj;

          Set<string> devNotExecute=new Set<String>();
        devNotExecute.add('TR007ManageWSIEM');

        PAD.doNotExecuteAfterMe(devNotExecute);

          System.assert(Obj.id!=null);
          set<string> ObjIds = new set<string>();
          ObjIds.add(Obj.id);

          // Tests DAL 
          Account a = DAL.getAccountById(acc.Id);
          a = DAL.getAccountByNumLDE('456');
          User u = DAL.getUserById('userId');
          Object__c s = DAL.getObjectById(Obj.Id);
            String agent = 'Momo';
            String BDNames = 'LDE_Name';
            String BDValues = 'LDE_Value';
            String url='http://213.139.98.70:8002/REST/CallManager/UpdateOpenCall/Agent?Switch=1'
                + '&Agent=' + EncodingUtil.urlEncode(agent,'UTF-8')
                + '&BDNames=' + EncodingUtil.urlEncode(BDNames,'UTF-8')
                + '&BDValues=' + EncodingUtil.urlEncode(BDValues,'UTF-8')
                + '&Overwrite=-1';
          Test.startTest();

            // Set mock callout class 
            Test.setMock(HttpCalloutMock.class, new MockHttpResponseGenerator());
            MockHttpResponseGenerator test1 = new MockHttpResponseGenerator();
            HttpRequest req = new HttpRequest();
            req.setEndpoint(url);
            system.assertEquals(req.getEndpoint(), url);
            req.setMethod('GET');
            Http h = new Http();                

            // Call method to test.
            // This causes a fake response to be sent
            // from the class that implements HttpCalloutMock. 
            TR004Object.GetIdObject(ObjIds);
            TR004Object.callWSGetIdObject(Obj);
            HttpResponse res = test1.respond(req);

            System.assert(CurrentUser.CRC__c=='WebHelp');

            //TR004Object.checkWsSuccessGetIdObject(res, Obj);
            // Verify response received contains fake values
            String contentType = res.getHeader('Content-Type');
            System.assert(contentType == 'text/xml');
            String actualValue = res.getbody();

            String expectedValue ='<Output xmlns=\"nice.uniform://\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\"><Data i:nil="true"/><Result xmlns:a=\"http://nice"><a:ResultCode>RESULTCODE</a:ResultCode><a:ResultMessage>No open call exists</a:ResultMessage><a:ResultCodeEx>CLS_SE_NO_OPEN_CALL</a:ResultCodeEx></Result><Exception i:nil="true" xmlns:a="http://schemas.datacontract.org/2004/07/System"/></Output>';
            System.assertEquals(actualValue, expectedValue);
            //System.assert(doc.load(expectedValue)!=null);
           // System.assert(doc!=null);
            String result = res.getbodyDocument().getRootElement().getChildElements()[1].getName();              
            String data= res.getbodyDocument().getRootElement().getChildElements()[0].getName();       
            String root = res.getbodyDocument().getRootElement().getName();

            System.assertEquals(200, res.getStatusCode());
            System.assertEquals('Result', result);
            System.assertEquals('Data', data);
            System.assertEquals('Output', root);

          Test.stopTest();
      }
      }

Best Answer

You're really close with the code you have. The mistake is that you're testing against the mock class after the Test.setMock instead of testing against your non-test class's wrapper method and callout method. The Test.setMock method tells the test framework to swap out the response data from your non-test class's callout.

You'll also need to remove the logic check you have in place in your callout method so it can properly populate the HttpResponse object.

public static void callWSGetIdObject(Object__c Obj ){
    // ...
              // remove this check, the test framework will swap the response data
                    if(!system.Test.isRunningTest()){
                    res = http.send(req);
                    }
    // ...
}

I can see you modeled this off of the documentation for the Test.setMock. The problem with their example is that their non-test method responds back with the HttpResponse object. That example is very basic since the method only returns the HttpResponse directly, but given the context of the feature, it is slightly tricky and confuses the issue.

https://www.salesforce.com/us/developer/docs/apexcode/Content/apex_classes_restful_http_testing_httpcalloutmock.htm

An easier example to think of is a REST service taking in 1 integer, and returning two integers, and your non-test method that performs the callout also processes it like most test accessible(public/global) callout methods tend to do. Like all other tests your job is to make sure the method itself (and its wrapper @future) function as expected when given the right inputs, and that their output is valid. Test.setMock is only used so you can populate the response data in the non-test method with expected data (also because test can't run callout). Ideally you'd write a few versions of this test with varying values and callout responses including a test that returned invalid responses or exception catching states (e.g. blank response, XML with no nodes except parent, missing an XML node that is critical).

[[example coming later, it's super late here]]

Related Topic