My Blog posts are like buses. You don’t see one for ages and then two come along at once.
I was musing, like you do, what to do when Unit Testing a service that makes successive call-outs to the same endpoint. If the endpoints were different then you could use the MultiStaticResourceCalloutMock class. But what if you need to make two calls to the same endpoint?
So take this bit of code:
public with sharing class MyCallout { public static Boolean doCallouts() { Boolean isDog = isDog('Toto'); Boolean isHuman = isHuman('Dorothy'); return isDog && isHuman; } private static Boolean isDog(String name) { return getSpecies(name) == 'dog'; } private static Boolean isHuman(String name) { return getSpecies(name) == 'human'; } private static String getSpecies(String name) { HttpRequest req = new HttpRequest(); req.setMethod('GET'); req.setHeader('Content-type', 'application/json'); req.setHeader('Accept', 'application/json'); req.setEndpoint('https://somewhereovertherainbow.com/wherestoto'); req.setBody('{"name" : "' + name + '"}'); Http http = new Http(); HttpResponse res = http.send(req); if (res.getStatusCode() != 200) { return null; } Map marshalledResponse = (Map)JSON.deserializeUntyped(res.getBody()); String species = (String)marshalledResponse.get('species'); return species; } }
It makes two successive calls to the same endpoint to resolve the species of a given Wizard Of Oz character. If they both match it returns true. Simple enough, but what about testing it?
In our unit test we create our mock response class (implementing the HttpCalloutMock interface) and add a simple constructor that takes a list of responses.
The respond method then returns the responses in sequence each time it’s called. Easy peasy lemon squeezy.
@isTest private class MyCalloutTest { @isTest static void testDoCallouts_pass() { // Create the responses in the correct sequence String[] responses = new String[] { '{ "species" : "dog" }', '{ "species" : "human" }' }; // Set mock callout class Test.setMock(HttpCalloutMock.class, new MockResponse(responses)); Boolean result = MyCallout.doCallouts(); System.assertEquals(true, result); } @isTest static void testDoCallouts_fail() { // Create the responses in the wrong sequence String[] responses = new String[] { '{ "species" : "human" }', '{ "species" : "dog" }' }; // Set mock callout class Test.setMock(HttpCalloutMock.class, new MockResponse(responses)); Boolean result = MyCallout.doCallouts(); System.assertEquals(false, result); } public class MockResponse implements HttpCalloutMock { private Integer m_responseIndex; private String[] m_reponses; public MockResponse(String[] reponses) { m_responseIndex = 0; m_reponses = reponses; } public HTTPResponse respond(HTTPRequest req) { HttpResponse res = new HttpResponse(); res.setHeader('Content-Type', 'application/json'); // Return the next response in the list String reponse = m_reponses[m_responseIndex]; m_responseIndex ++; res.setBody(reponse); res.setStatusCode(200); return res; } } }
You could adapt this pattern to provide different response codes in the same way.