5

I'm trying to unit test the behavior of some code which performs callouts and its logic is dependent on the HTTP status code of the response - 200 vs 401.

The pattern being exercised is familiar:

  1. Make callout
  2. Response is (unauthorized) HTTP 401
  3. Logic checks Status value on the response
  4. if (status == 200) { /* yay! */ }
  5. if (status == 401) { /* make request for auth /* }
    • retry original callout with newly acquired authorization
    • yay!

I am attempting to test the code's "try, auth, retry" behavior using the MultiStaticResourceCalloutMock but it appears that only a single status code can be set for all requests that the mock provider can respond to using the .setStatusCode(int) method. Is this accurate?

Is there a mechanism for using a single MultiStaticResourceCalloutMock and having it respond with different status codes per URL in the test context?

Is the only solution here to use a Dispatcher such as what Andrew Fawcett documents here in this answer? https://salesforce.stackexchange.com/a/19263/660

Is there another method/mechanism that allows for different status codes to be returned by the mock provider on the HTTPResponse?


Some generic testing code from Salesforce showing the one-time usage pattern of the .setStatusCode(200) method and the multimock class.

@isTest
private class CalloutMultiStaticClassTest {
    @isTest static void testCalloutWithMultipleStaticResources() {
        // Use MultiStaticResourceCalloutMock to
        // specify fake response for a certain endpoint and 
        // include response body in a static resource.    
        MultiStaticResourceCalloutMock multimock = new MultiStaticResourceCalloutMock();
        multimock.setStaticResource(
            'http://api.salesforce.com/foo/bar', 'mockResponse');
        multimock.setStaticResource(
            'http://api.salesforce.com/foo/sfdc', 'mockResponse2');
        multimock.setStatusCode(200);
        multimock.setHeader('Content-Type', 'application/json');

        // Set the mock callout mode
        Test.setMock(HttpCalloutMock.class, multimock);

        // Call the method for the first endpoint
        HTTPResponse res = CalloutMultiStaticClass.getInfoFromExternalService(
            'http://api.salesforce.com/foo/bar');
        // Verify response received 
        System.assertEquals('{"hah":"fooled you"}', res.getBody());

        // Call the method for the second endpoint
        HTTPResponse res2 = CalloutMultiStaticClass.getInfoFromExternalService(
            'http://api.salesforce.com/foo/sfdc');
        // Verify response received 
        System.assertEquals('{"hah":"fooled you twice"}', res2.getBody());
    }
}
1
  • I've done things similar to the Fawcett solution; each error condition has to be tested in a separate method using a common MockResponse generator where you pass into the constructor the status code you want to simulate
    – cropredy
    Commented Jan 14, 2016 at 2:26

1 Answer 1

15

I personally use a more useful version of the HttpCalloutMock that supplies responses in the order I specify. It basically looks like this:

public class QueueHttpMock implements HttpCalloutMock {
    HttpResponse[] responses = new HttpResponse[0];
    public void addResponse(HttpResponse response) {
        responses.add(response);
    }
    public HttpResponse respond(HttpRequest request) {
        return responses.remove(0);
    }
}

I can then use this in my unit tests to create multiple responses:

QueueHttpMock mock = new QueueHttpMock();
HttpResponse res1 = new HttpResponse();
// fill out values here
HttpResponse res2 = new HttpResponse();
// fill out values here
mock.addResponse(res1);
mock.addResponse(res2);
Test.setMock(HttpCalloutMock.class, mock);

Using this framework allows me to mock multiple statuses, bodies, etc, as I need to. It also makes the framework suitable for all of the test classes I write that need them. I tend to use this for classes that perform multiple callouts in the same method.

4
  • 1
    Just pointing the typo: I think it should be mock.addResponse(res1);
    – javanoob
    Commented Jan 23, 2016 at 13:27
  • 2
    @javanoob I'm not sure how I missed this comment, but I've updated the answer. Thanks!
    – sfdcfox
    Commented Nov 1, 2016 at 15:24
  • @sfdcfox When you mention //fill out values here , what are the values that needs to be filled. The parameters for the calling the callout method in the apex class??
    – user81642
    Commented Jun 3, 2020 at 2:43
  • @sfdcfox Nevermind got it working. Thank you so much for this answer
    – user81642
    Commented Jun 3, 2020 at 2:48

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .