feed-icon-32x32.png

Subscribe in Feed Reader
Subscribe by Email

Learn how Salesforce.com can help your business

Testing HTTP Callouts

When writing Apex Code, there are some things that simply cannot be tested, namely HTTP callouts.  The Intro to Apex Code Test Methods has one solution to testing as much of an HTTP callout as possible.  That is to break the HTTP callout into 3 methods: 1) Build Request, 2) Make Request, 3) Handle Response.  In the test method, you are only calling the Build Request and Handle Response methods.

I have another approach.  My main reason for not using the approach mentioned above is that I didn’t read that post until after I developed something.  However, my approach accomplishes the same thing, but also adds a variable to your class so that you can know whether or not a test is running.  Knowing this may be useful in other situations too.

Here’s is a class that uses my technique.

public class sample {

	// Static variable that assumes a test is not running
	public static boolean isApexTest = false;

	public String main(){

	    // Do a whole bunch of stuff

		// Build the http request
		Http h = new Http();
		HttpRequest req = new HttpRequest();
	    req.setEndpoint('http://local.yahooapis.com/MapsService/V1/geocode?appid=YD-9G7bey8_JXxQP6rxl.fBFGgCdNjoDMACQA--&street=701+First+Ave&city=Sunnyvale&state=CA');
	    req.setMethod('GET');

		// Invoke web service call
		String result = '';
		if (!isApexTest){
			// Make a real callout since we are not running a test
			HttpResponse res = h.send(req);
			result = res.getBody();
		} else {
			// A test is running
			result = '<?xml version="1.0"?><ResultSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:yahoo:maps" xsi:schemaLocation="urn:yahoo:maps http://api.local.yahoo.com/MapsService/V1/GeocodeResponse.xsd"><Result precision="address"><Latitude>37.416397</Latitude><Longitude>-122.025055</Longitude><Address>701 1st Ave</Address><City>Sunnyvale</City><State>CA</State><Zip>94089-1019</Zip><Country>US</Country></Result></ResultSet>';
		}

		// Do whole bunch of stuff

	    return result;
	}

	// Wrapper method for "main" that we will call in the Test Class
	public String mainForTest(){
		isApexTest = true;
		return main();
	}
}

I have a static variable called isApexTest that I set to false by default so the code assumes a test is not running.  For testing purposes, I created a “wrapper” method (called “mainForTest()”) that I will call in my test class in order to test the main() method. This wrapper method sets the isApexTest variable to true, calls the main() method and then passes back the result from the main() method. mainForTest() is merely a way to get the isApexTest variable set to true.

My test class is below.

@isTest
private class sample_test {

    static testMethod void main_test() {

		// Establish sample class
		sample s = new sample();

		// Call the mainForTest wrapper method so we can set the variable indicating that a test is running
		String retVal = s.mainForTest();

	 	// Add asserts for validation
	 	String expectedResult = '<?xml version="1.0"?><ResultSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:yahoo:maps" xsi:schemaLocation="urn:yahoo:maps http://api.local.yahoo.com/MapsService/V1/GeocodeResponse.xsd"><Result precision="address"><Latitude>37.416397</Latitude><Longitude>-122.025055</Longitude><Address>701 1st Ave</Address><City>Sunnyvale</City><State>CA</State><Zip>94089-1019</Zip><Country>US</Country></Result></ResultSet>';
	 	System.assertEquals(retVal, expectedResult);

	 }

}

In case you are wondering why I use a static variable for isApexTest instead of a class property, it is because I originally developed this technique to test an Apex Web Service. Since all web service methods are static, I couldn’t create an instance of a class to set the property to true and then call the web service method. Web service methods must be invoked directly and not through an instance of a class. That’s where the wrapper method idea came from.

Another approach than a wrapper method would be to have an input variable to my main() method that accepts true/false whether a test was running.  I did not go with this approach because I might have many methods performing callouts and I preferred to have 1 public variable accessible throughout the entire class than a local variable in each method. If I used a local variable, I would need to keep passing it around to other methods.

Comment with your thoughts and alternative approaches.

6 Comments »

  1. Jeff Douglas Said,

    May 4, 2009 @ 11:38 am

    Very nice way to work “with” the Salesforce.com test framework. I tip my virtual hat to you sir.

  2. Rajesh Shah Said,

    May 18, 2009 @ 3:05 am

    This is a nice way to handle test methods for Apex Callouts. However I would still prefer the other way of dividing the HTTP callout into 3 methods. This way I am not adding any extra code to what is actually required to run the method. All my extra stuff required for testing the callout is in the test method. Another reason is that it is good to have the callout modularised into 3 separate parts. This allows me make the call from different functions and send the response to a single function.

  3. Gabe Said,

    August 13, 2009 @ 8:54 am

    Great idea dude. So much better than any of the SFDC developers could give me. Web services are obviously still a big mystery to them.

  4. Calling a REST Web Service (JSON) with Apex | Jeff Douglas – Technology, Coding and Bears… OH MY! Said,

    January 6, 2010 @ 5:23 am

    [...] unit tests for callouts can present a challenge. Scott Hemmeter has a really good article entitled Testing HTTP Callouts which should provide you with some useful techniques. You should also check out An Introduction to [...]

  5. codeulike Said,

    May 27, 2010 @ 8:15 am

    Is using the static variable like that thread-safe? What happens if someone else calls the call-out at the same time you’re running the test? Or afterwards, for that matter, given that you don’t seem to set the flag back to false?

  6. Scott Hemmeter Said,

    May 27, 2010 @ 8:23 am

    I am pretty sure it’s no issue. In the next transaction, the variable is reset. Every transaction is unique so the next callout has its own set of variable instances.

RSS feed for comments on this post · TrackBack URI

Leave a Comment

All comments are moderated. Other visitors will not see your comment until it has been approved.