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.

  1. IHttpResponse

  2. 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:

Let’s Talk

Drop a note below to move forward with the conversation 👇🏻

Abhinav Gupta

First Indian Salesforce MVP, rewarded Eight times in a row, has been blogging about Salesforce, Cloud, AI, & Web3 since 2011. Founded 1st Salesforce Dreamin event in India, called “Jaipur Dev Fest”. A seasoned speaker at Dreamforce, Dreamin events, & local meets. Author of many popular GitHub repos featured in official Salesforce blogs, newsletters, and books.

https://abhinav.fyi
Previous
Previous

JSON parsing in Apex, a developer’s nightmare :( Winter’12 release I badly need you !

Next
Next

Apex inheritance – Extending managed package abstract/virtual classes & interfaces !