Dependency Injection for ease in Testing Apex WebService Callouts!
Dear Apex developers, who are using HTTP Restful Classes know that testing the callouts is not allowed in Apex. People came up with their own smart ways to handle this restriction.
For example: Scott from ArrowPointe suggested this nice approach. I found some scope for improvement in this approach for a couple of reasons:
It can be simplified now after the availability of the Test.isRunningTest() method; this method came in a recent Salesforce release and was not available at the time Scott wrote the post (May 2009).
Testing the web service code with a variety of XML/JSON responses, status codes, and response headers. One can change it to return different XML responses for each test method, but using a variety of status codes, response headers, etc. is not an easy job.
Fixing the problem with Dependency Injection
Dependency Injection is a cool pattern to reduce coupling. In this scenario, the biggest coupling to break was the dependency on the Apex HTTP class to make the callout. Here is my attempt to do the same using Manual Dependency Injection.
To do the same, I defined contracts for two key players/dependencies in the HTTP callout flow, i.e.
IHttpResponse
IHttpCallout
Please Note: Throughout the code samples coming below, I tried adding a lot of inline comments rather than blog text to make the explanation easy.
1. IHttpResponse
As the name indicates, it’s an interface that defines the contract for HTTP response. For the least learning curve, all methods of this interface are exactly similar to what the Apex HttpResponse class offers.
/**
Contract for HTTPResponse. To avoid learning and confusions this interface is exposing
all the methods available in Apex HTTPResponse class(http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_classes_restful_http_httpresponse.htm)
Test classes can override and implement them as required
*/
public interface IHttpResponse {
String getBody();
Dom.Document getBodyDocument();
String getHeader(String key);
String[] getHeaderKeys();
String getStatus();
Integer getStatusCode();
Xmlstreamreader getXmlStreamReader();
// have to name it toStrings() instead of toString(), as the later
// is reserved by Apex
String toStrings();
}
2. IHttpCallout
This interface abstracts out the real web service action, i.e., the calling mechanism. This calling mechanism is the point where Apex test code is limited and can’t do. In normal Apex, we achieve this callout using the HTTP class as follows:
Http http = new Http();
HttpRequest req = new HttpRequest();
req.setEndpoint('http://www.tgerm.com/mycoolws.xml');
req.setMethod('GET');
// This is where Apex tests are restricted
HttpResponse res = http.send(req);
This interface has a single method with arguments similar to the Apex Http class; the only difference is in the return type, i.e., IHttpResponse.
/**
Contract for a simple web service callout using Apex.
Only a single method is available for abstracting the stuff out for ease of Testing.
Test classes can provide implmentations of this interface to return custom/fake/mock HTTP responses.
*/
public interface IHttpCallout {
/**
Accepts a ready to send requests and makes a callout using that.
*/
IHttpResponse send(HttpRequest req);
}
Implementing Contracts for both Real-World & Test Simulation!
OK, we have figured out dependencies now and defined a clear contract for both too. Now it's time to implement the same contract for both the Apex real and test execution.
Real-World Implementation
DefHttpResponse implements IHttpResponse
This implementation is a simple wrapper over the existing Apex HTTPresponse class.
/**
Default wrapper implementation over standard Apex HttpResponse class.
Reference : http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_classes_restful_http_httpresponse.htm
All contract methods of IHttpResponse are delegated to the wrapped Apex HttpResponse instance.
*/
public virtual class DefHttpResponse implements IHttpResponse {
public Httpresponse resp;
public DefHttpResponse(HttpResponse resp) {
this.resp = resp;
}
public String getBody() {
return resp.getBody();
}
public Dom.Document getBodyDocument() {
return resp.getBodyDocument();
}
public String getHeader(String key) {
return resp.getHeader(key);
}
public String[] getHeaderKeys() {
return resp.getHeaderKeys();
}
public String getStatus() {
return resp.getStatus();
}
public Integer getStatusCode() {
return resp.getStatusCode();
}
public Xmlstreamreader getXmlStreamReader() {
return resp.getXmlStreamReader();
}
public String toStrings() {
return resp.toString();
}
}
DefHttpCallout implements IHttpCallout
This implementation is a delegate again to the standard Apex HTTP class for the critical “send(HttpRequest)” operation.
Please NOTE: DefHttpCallout.send() doesn’t return HTTPResponse, instead it's returning IHttpResponse.
/**
Default implementation meant for use in actual apex code. It runs out of
standard Apex Http Class (http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_classes_restful_http_http.htm)
*/
public class DefHttpCallout implements IHttpCallout {
public IHttpResponse send(HttpRequest req) {
Http http = new Http();
return new DefHttpResponse(http.send(req));
}
}
The whole package of above interfaces/classes is available as GIST on GitHub.
Sample callout to Amazon Web Services
The AWS class below explains it all in full glory. This class restructures the same good AWS callout example available on Salesforce Developer here. The same is available as GIST here.
public with sharing class AWS {
/**
Handle to easily switch between natural HTTP callout and Mock Callouts for test cases.
Those who love patterns, can use Factory if required to achieve the same. But I find
it really handy in test cases to change.
for ex. My test code can just replace the IHttpCallout impl as:
AWS.CALLOUT = new MockAWSCallout();
Here MockAWSCallout is a fake implementation that will simulate HTTP callout for sake of test cases.
*/
public static WS.IHttpCallout CALLOUT = new WS.DefHttpCallout();
public String serviceEndPoint = '';
public String bucket = '';
public String key = '';
/*
Example AWS callout code taken from this awesome WIKI page :
http://wiki.developerforce.com/index.php/Apex_Web_Services_and_Callouts
Please Note: this method is just for illustration purpose, not ready to be used as it is.
*/
public void store(String body) {
HttpRequest req = new HttpRequest();
//Set HTTPRequest Method
req.setMethod('PUT');
//Set HTTPRequest header properties
req.setHeader('content-type', 'image/gif');
req.setHeader('Content-Length','1024');
req.setHeader('Host','s3.amazonaws.com');
req.setHeader('Connection','keep-alive');
req.setEndpoint( this.serviceEndPoint + this.bucket +'/' + this.key);
//Set the HTTPRequest body
req.setBody(body);
try {
//Execute web service call here
//
// PLEASE NOTE : here we have used the static variable
// CALLOUT here
//
WS.IHttpResponse res = CALLOUT.send(req);
//Helpful debug messages
System.debug('STATUS:'+res.getStatus());
System.debug('STATUS_CODE:'+res.getStatusCode());
/// Do what else is biz requirement with the response.
// ...
//.....
//
} catch(System.CalloutException e) {
//Exception handling goes here....
}
}
}
Testing Simulation for Amazon Sample above
MockHttpResponseBase implements IHttpResponse
For starting up with test cases, an intermediate base class is introduced that gives a complete virtual implementation of IHTTPResponse. By complete virtual, I mean, any operation will throw exceptions, as it's virtual. This class is meant to give a full virtual implementation of IHTTPResponse so that test cases can only override the response methods as required. For example, in most of the cases, we need only HttpRespone.getBody() or HttpResponse.getStatusCode().
/**
Indicator for operation being accessed is virtual as of now.
*/
public class VirtualOperationException extends Exception {}
/**
Meant to be base/parent class for Apex test case simulations.
Its a mock implementation of IHttpResponse, it gives a virtual body
of all contract methods in IHttpResponse, and throws VirtualOperationException
for every method call. Using this class as parent, subclasses would be easy i.e. just
override the methods required, instead of implementing the whole IHttpResponse contract.
For ex. in most of the cases, one will override getStatusCode() and getBody() for testing purposes.
*/
public virtual class MockHttpResponseBase implements IHttpResponse {
public virtual String getBody() {
throw new VirtualOperationException('No implementation available !');
}
public Dom.Document getBodyDocument() {
throw new VirtualOperationException('No implementation available !');
}
public virtual String getHeader(String key) {
throw new VirtualOperationException('No implementation available !');
}
public virtual String[] getHeaderKeys() {
throw new VirtualOperationException('No implementation available !');
}
public virtual String getStatus() {
throw new VirtualOperationException('No implementation available !');
}
public virtual Integer getStatusCode() {
throw new VirtualOperationException('No implementation available !');
}
public virtual Xmlstreamreader getXmlStreamReader() {
throw new VirtualOperationException('No implementation available !');
}
public virtual String toStrings() {
throw new VirtualOperationException('No implementation available !');
}
}
This class and fixture are common to all Apex test cases. So its body is kept together in the same Apex class, i.e., WS here.
MockHTTPResponse extends WS.MockHTTPResponseBase
For the AWS sample, we need to work with three response parameters, i.e., body, status, and status code. So this class utilizes the MockHttpResponseBase and gives implementation of the required methods.
/**
Please note we have extended WS.MockHttpResponseBase
instead of implementing ws.IHttpResponse, because we want to
override three methods only
*/
public class MockHttpResponse extends WS.MockHttpResponseBase {
public String body;
public String status;
public Integer statusCode;
public MockHttpResponse(String body, String status, Integer statusCode) {
this.body = body;
this.status = status;
this.statusCode = statusCode;
}
public override String getBody() {
return body;
}
public override String getStatus() {
return status;
}
public override Integer getStatusCode() {
return statusCode;
}
}
MockHttpCallout implements WS.IHttpCallout
This gives a mock implementation of the callout behavior. Here various test methods can tweak it easily to return the required Mock responses.
/**
Mock Callout Implementation
*/
public class MockHttpCallout implements WS.IHttpCallout {
private MockHttpResponse resp;
public WS.IHttpResponse send(HttpRequest req) {
return resp;
}
/**
This method was not part of original WS.IHttpCallout contract
as its one of the way test case can pass mock response to it.
*/
public void setResponse(MockHttpResponse resp) {
this.resp = resp;
}
}
Sample testXXX() method using this fixture
/*
A test call to the store method in AWS class
*/
static testMethod void testStoreCall() {
MockHttpCallout mockCallout = new MockHttpCallout();
// Tell AWS Apex class to use Mock Callout instead of this one
AWS.CALLOUT = mockCallout;
AWS amazon = new AWS();
// create a mock XML response you want the actual code to parse
MockHttpResponse mockResp = new MockHttpResponse(' some xml response body', 'OK', 200);
// tell callout to return this response when a request comes
mockCallout.setResponse(mockResp);
amazon.store('My Cool Body to preserve in Amazon S3 :)');
// Please do some assertions
//
System.assertEquals('Some Good Asserts here', 'No good asserts, writing those will be out of scope for this illustration');
//
}
Did You Notice?
I didn’t use Test.isRunningTest() anywhere in the code above to know if the code is in Test Context. This is a good API, but Apex gives decent inheritance and polymorphic behavior to achieve the same.
Source Code as GIST
The full source is available as GIST here: https://gist.github.com/1238904.
To explore the code, please start in this order:
WS.cls: WS is a short form of WebService; it’s a sort of package class having all the core interfaces and default implementation classes as child classes.
AWS.cls: A class having sample callout illustration to Amazon S3. This class uses the WS.cls fixture.
Test_AWS.cls: Test cases indicating how the WS.cls fixture can be used for mocking up the callouts.
Feedback & Views
I hope this fixture will help make your life simple with Apex Web Service callouts and testing. Looking forward to your views on this.
References:
Testing HTTP Callouts - Scott Hemmeter
Dependency injection - Wikipedia
Let’s Talk
Drop a note below to move forward with the conversation 👇🏻