4

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());
    }
}
  • 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 Jan 14 '16 at 2:26
9

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.

  • 1
    Just pointing the typo: I think it should be mock.addResponse(res1); – javanoob Jan 23 '16 at 13:27
  • 1
    @javanoob I'm not sure how I missed this comment, but I've updated the answer. Thanks! – sfdcfox Nov 1 '16 at 15:24

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.