September 24, 2011

Dependency Injection for ease in Testing Apex WebService Callouts !

Apex developers, who are using Http Restful classes know that testing the callouts is not allowed in Apex. People came up with there own smart ways to handle this restriction. For ex.
Scott from ArrowPointe suggested this nice approach : http://sfdc.arrowpointe.com/2009/05/01/testing-http-callouts/
I found some scope of improvement in this approach for a couple of reasons like
  • It can be simplified now after availability of Test.isRunningTest() method, this method came in recent Salesforce release and was not available at the time Scott wrote the post(May 2009).
  • Testing the web service code with 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 variety of status codes, response headers etc is not 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 dependency on 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 contract for two key players/dependencies in 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 blog text to make the explanation easy.

1. IHttpResponse

As name indicates its an interface that defines contract for HTTP response. For least learning curve, all methods of this interface are exactly similar to what 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 webservice action i.e. the calling mechanism. This calling mechanism is the point where Apex test code is limited and it can’t do. In normal Apex we achieve this callout using 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 is having a single method with arguments similar to the Apex Http class, 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 contract for both Real World & Test Simulation !

OK, we have figured out dependencies now and defined a clear contract for both too. Now its 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 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 standard Apex Http class for the critical “send(HttpRequest)” operation. Please note DefHttpCallout.send() doesn’t returns HttpResponse, instead its 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 here : https://gist.github.com/1238904#file_ws.java
Sample callout to Amazon Web Services
AWS class below explains all in full glory. This class restructures the same good AWS callout example available on DeveloperForce WIKI here : http://wiki.developerforce.com/index.php/Apex_Web_Services_and_Callouts
The same is available as GIST here : https://gist.github.com/1238904#file_aws.java
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 = '<SET IT HERE>';
public String bucket = '<SET IT HERE>';
public String key = '<SET IT HERE>';
/*
 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 exception, as its 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 ex. in most of the cases we need only HttpRespone.getBody() or HttpResponse.getStatusCode() only.
/**
  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 is common to all apex test cases. So its body is kept together in same Apex class i.e. WS here : https://gist.github.com/1238904#file_ws.java
MockHttpResponse extends WS.MockHttpResponseBase
For the AWS sample, we need to work with three response param i.e. body, status and statuscode. 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 to 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('<xml..> 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 noticed ?

I didn’t used 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

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 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 making life simple with Apex Web Service callouts and testing. Looking forward for your views on this.