17

I am facing a issue with my mock class where I am suppose to create a mock class for a class that makes several callouts in a single method based on criteria's. Now , when I am trying to create a mock class then it is only acting for the first callout but others callouts are not getting handled.How to work with these classes.

3 Answers 3

30

Here is perhaps the simplest form of a mock that deals with multiple requests.

It is declared as an inner class of the test class so the idea is that it handles all the cases that the outer test class handles. In the respond method you can look at the endpoint of the request or the body of the request and use if/else logic to return the appropriate fake response. (You can also add asserts to verify the details of the request body.)

@IsTest
private class CalloutTest {

    private class Mock implements HttpCalloutMock {
        public HTTPResponse respond(HTTPRequest req) {
            if (req.getEndpoint().endsWith('abc')) {
                HTTPResponse res = new HTTPResponse();
                res.setBody('{}');
                res.setStatusCode(200);
                return res;
            } else if (req.getEndpoint().endsWith('xyz')) {
                ...
            } else {
                System.assert(false, 'unexpected endpoint ' + req.getEndpoint());
                return null;
            }
        }
    }

    @IsTest
    static void abc() {
        Test.setMock(HttpCalloutMock.class, new Mock());
        ...
    }

    @IsTest
    static void xyz() {
        Test.setMock(HttpCalloutMock.class, new Mock());
        ...
    }
}
2
  • 2
    I would argue a Map is simpler than an if/else chain.
    – Adrian Larson
    Sep 1, 2016 at 16:41
  • 1
    @AdrianLarson Only if you overlook that the map values have to be other HttpCalloutMock implementations.
    – Keith C
    Sep 1, 2016 at 17:22
28

This need is covered in Testing Apex Callouts using HttpCalloutMock. Specifically, see the Testing multiple HTTP callouts section. Here's the example they use:

public class MultiRequestMock implements HttpCalloutMock {
    Map<String, HttpCalloutMock> requests;

    public MultiRequestMock(Map<String, HttpCalloutMock> requests) {
        this.requests = requests;
    }

    public HTTPResponse respond(HTTPRequest req) {
        HttpCalloutMock mock = requests.get(req.getEndpoint());
        if (mock != null) {
            return mock.respond(req);
        } else {
                throw new MyCustomException('HTTP callout not supported for test methods');
        }
    }

    public void addRequestMock(String url, HttpCalloutMock mock) {
        requests.put(url, mock);
    }
}

I'm not sure why they don't annotate the class as @IsTest. You probably should.

1

I've created a generic "utility" class that makes this much easier to do (and all HTTP testing in general).

Basically the setup looks like this:

MockHttpResponseHandler.SimpleMockResponse getResp = new MockHttpResponseHandler.SimpleMockResponse('GET', getResponse);

MockHttpResponseHandler.SimpleMockResponse errResp = new MockHttpResponseHandler.SimpleMockResponse('GET', null);
errResp.statusCode = 500;

MockHttpResponseHandler mock = new MockHttpResponseHandler();
mock.addResponse(testEndpoint, getResp);
mock.addResponse(testEndpoint, getResp);
mock.addResponse(testEndpoint, errResp);

// when hitting GET:testEndpoint, 'getResp' will be returned twice.  All additional calls will return 'errResp'

It also allows much more dynamic functionality if needed through custom implementation.

Check out the repo readme for full details on how it works.

You must log in to answer this question.

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