Dec 22, 2014

Test Coverage Pattern for Multi-Callout Methods

Visit our website


When you're developing Apex code for integrations with external systems, an issue you always need to overcome is the creation of test coverage to cover your various methods responsible for making callouts to one or more endpoints.

Existing Resources

Salesforce provides a few different ways for you to achieve this:
  1. HttpCalloutMock Interface
  2. StaticResourceCalloutMock
  3. MultiStaticResourceCalloutMock


Problem

However, all three have a similar shortcoming when additional complexity is needed.  With systems integrations, it's not uncommon to require multiple callouts within the same execution.  The three examples from Salesforce can handle that just fine... as long as you don't need to use the same endpoint more than once AND expect different results.

Before we take a look at a solution, here are some details on the existing testing mechanisms and sample usages from the Salesforce documentation.


HttpCalloutMock Interface

The HttpCalloutMock Interface allows you to create a respond() method in a test utility class where a response is constructed.  Within the test coverage class, you tell your test to use the mock utility with a test.setMock() method.

TestUtility (from documentation)


In that code snippet, note the "implements HttpCalloutMock" interface declaration.  HttpCalloutMock requires a respond() method, which accepts an HttpRequest parameter.  Within this method, an HttpResponse is constructed.

Test Method Usage (from documentation)

If you look at their comments within the respond() method, you could intelligently create an HttpResponse based on the request - however, for that endpoint, you'll always receive the same response.  That's not ideal if you're making multiple calls within an execution context and need different results.  Just a few examples might include testing paging ("next_page":2), date/time stamp requirements (if date/time > last received date/time), record count calculations (count # increase after POST), and so on.

StaticResourceCalloutMock and MultipleStaticResourceCalloutMock 

Using these methods, you can leverage Static Resources to maintain you response bodies, which can help keep your Apex code nice and tidy. Rather than implement an HttpCalloutMock interface, you can declare everything within your test coverage.  Here are usage examples of both the single StaticResourceCalloutMock and the MultipleStaticResourceCalloutMock

Test Method Usage of Static ResourceCalloutMock (from documentation)


Test Method Usage of MultipleStaticResourceCalloutMock (from documentation)


Solution

So how do we go about setting up a mechanism to achieve test coverage in a method that requires multiple callouts, including multiple callouts to the same resources where different resutlts are expected?  We'll leverage and extend the first solution, the HttpMockCallout interface.  We'll define a constructor that accepts a map of callout methods to callout endpoints to a list of response details.  We'll also accept a boolean to control whether or not the responses should be re-used or thrown away so a different response can be provided next time. Let's start with a sample call we'll be covering: Here we have two methods (doCallout1 and doCallout2) that make a GET callout to two different endpoints (/resources/example1 and /resources/example2).  We also have a doCallouts() method that uses those callout methods; it calls Callout1 twice and Callout2 once.  It then returns a concatenated string of each callout's response body. If we test without any customizations, using the standard mock interface, here's what it would look like:

Test Utility


Test Class

Our output would be:
{"example":"response1"}:{"example":"response1"}:{"example":"response2"}
Instead of:
{"example":"response1"}:{"example":"response1b"}:{"example":"response2"}
If we modify the Test Utility, can can get the expected results...

Test Utility

While we maintain the use of the HttpCalloutMock interface, we extend it's functionality by providing a new object called "Resp" that will hold individual response bodies, statuses, status codes, and a boolean called "Discard."  
A nested map, called ResponseMap, will be used to pair callout methods to endpoints and the endpoints to a list of these "Resp" records.
Method --> Endpoint --> LIST<Resp>
Within the respond() method of the interface, we'll get a list of Resp's/responses from the map using the provided HttpRequests (from the respond() signature's HttpRequest param), and use the Resp at the top of the list to populate a newly instantiated HttpResponse's details (set its body, result, and result code).
To help with our original problem of being able to provide different responses to calls, using the same endpoints, the "discard" boolean will be used to remove a resp, once moved, from the list, so in subsequent calls, another resp is used to populate the HttpResponse.


Test Method

The test method is only slightly different.  Before using the test.setMock() method, we have to load up the ResponseMap with the responses that are required for the testing in that method.  Now, within our test method, we can set up everything that's needed, from multiple methods, endpoints, with varying response bodies and results, as needed.



Now you can run the test class and get the expected results:
{"example":"response1"}:{"example":"response1b"}:{"example":"response2"}

Dec 14, 2014

What Color Is It?

While swiping away at my tablet, like a madman with my morning cup-o-joe, I came across this novelty of a site:  http://whatcolourisit.scn9a.org/.


The idea is simple... take the hour, minute, and second of the current time and use those values combined as the page background's hex color (#hhmmss).

Admittedly, there's not a lot of business purpose here, but it can be a good development exercise to use as an introduction to re-rendering page components using actionPollers and a few various Visualforce functions.

Can it be done?  With the exception of the minimal polling time being 5 seconds, you know it! 

Here's the end result:



Here's the Visualforce page: