17

Here is some background- I have an apex future method which is being called from an trigger. The future method then performs 3 different http calls to an external system (same endpoints). All requests have different body. Here is how it looks like-

public Static Void MyCallOut{
   String session= callout1();
   callout2(session);
   If(condition)   
      callout3(session);
   else
      callout4(session);
}

Everything works well.

I am trying to write test class for this using "HttpCalloutMock Interface", and using this as reference.
Here is my MockHttpResponseGenerator, which I intend to call from my test class-

@isTest
global class MockHttpResponseGenerator implements HttpCalloutMock{
    // Implement this interface method
    global HTTPResponse respond(HTTPRequest req) {

        // Create a fake response
        HttpResponse res = new HttpResponse();
        res.setHeader('Content-Type', 'text/xml');
        res.setBody('<soapenv:Envelope>'+
                '<soapenv:Body><re:sessionId>BBE099E0392E6509B43</re:sessionId>'+
                '</soapenv:Body></soapenv:Envelope>');
        res.setStatusCode(200);
        return res;
    }
}

Now my questions is, How can I or Is there a way to make it work so that the HttpCalloutMock returns different responses for different calls? or Is there something I am missing?

P.S. I know this can be achieved by having separate mockcallresponse class to be called from the main class when isRunningTest is true. But, I want to if the same can be achieved without contaminating my main code.

Thanks

  • 2
    If the endpoints were different, you could've used the MultiStaticResourceCalloutMock (salesforce.com/us/developer/docs/apexcode/Content/…). If not, you could set a static variable and the mock response class could return a response based on the value you've set, e.g. staticCalloutMethodName = 'Callout1'; – techtrekker Jun 28 '13 at 20:22
  • You could also just set the endpoints as different values when Test.isRunningTest and use MultiStaticResourceCalloutMock – techtrekker Jun 28 '13 at 20:34
  • @techtrekker : thanks for good suggestion, but not ideal as it will still involve executing separate lines of code for test. – Sai Jun 28 '13 at 20:51
  • 1
    Not with his first suggestion it won't. If you have a public static string in your class (the non test class), and your http methods simply set the value of this string to the name of the method, then in your mock data class you can read that static variable and return different mock data based on the string. The only addition to your normal code is the setting of the string value, the rest is all done in your test code in the mock interface. – drakored Jun 28 '13 at 22:28
  • I guess my confusion is due to my lack of understanding of how the httpcallout behaves, so please bear with me. But, how or will the Mockclass read the value of static string even if the string is not part of the actual httpsend request? I will try this at the earliest.Thanks!! – Sai Jun 28 '13 at 22:53
25

Salesforce passes in the HTTPRequest made by the logic your testing into the mock implementation to help with this type of requirement. So in the HttpCalloutMock.respond method implementation. You can call req.getBody() and condition the response based on that, here is an example...

@IsTest
private with sharing class MultiHttpMockTest 
{
    public class MockHttpResponseGenerator implements HttpCalloutMock
    {
        public HTTPResponse respond(HTTPRequest req) 
        {   
            HttpResponse res = new HttpResponse();
            res.setHeader('Content-Type', 'text/xml');
            if(req.getBody().contains('Call 1'))
                res.setBody('<soapenv:Envelope><soapenv:Body>Response 1</soapenv:Body></soapenv:Envelope>');
            else if(req.getBody().contains('Call 2'))
                res.setBody('<soapenv:Envelope><soapenv:Body>Response 2</soapenv:Body></soapenv:Envelope>');
            else if(req.getBody().contains('Call 3'))
                res.setBody('<soapenv:Envelope><soapenv:Body>Response 3</soapenv:Body></soapenv:Envelope>');            
            res.setStatusCode(200);
            return res;
        }
    }

    public static testmethod void testMultiMockResponse()
    {
        // Set single mock implementation that responds dynamically to different requests
        Test.setMock(HttpCalloutMock.class, new MockHttpResponseGenerator());

        // Frst request
        Http h = new Http();        
        HttpRequest req1 = new HttpRequest();
        req1.setEndpoint('http://mygreatservice.com/foo/bar');
        req1.setMethod('POST');
        req1.setBody('<soapenv:Envelope><soapenv:Body>Call 1</soapenv:Body></soapenv:Envelope>');
        HttpResponse res1 = h.send(req1);
        System.assertEquals('<soapenv:Envelope><soapenv:Body>Response 1</soapenv:Body></soapenv:Envelope>', res1.getBody());

        // Second request       
        HttpRequest req2 = new HttpRequest();
        req2.setEndpoint('http://mygreatservice.com/foo/bar');
        req2.setMethod('POST');
        req2.setBody('<soapenv:Envelope><soapenv:Body>Call 2</soapenv:Body></soapenv:Envelope>');
        HttpResponse res2 = h.send(req2);
        System.assertEquals('<soapenv:Envelope><soapenv:Body>Response 2</soapenv:Body></soapenv:Envelope>', res2.getBody());

        // Third request        
        HttpRequest req3 = new HttpRequest();
        req3.setEndpoint('http://mygreatservice.com/foo/bar');
        req3.setMethod('POST');
        req3.setBody('<soapenv:Envelope><soapenv:Body>Call 3</soapenv:Body></soapenv:Envelope>');
        HttpResponse res3 = h.send(req3);
        System.assertEquals('<soapenv:Envelope><soapenv:Body>Response 3</soapenv:Body></soapenv:Envelope>', res3.getBody());                    
    }
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.