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.

17 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.

  7. Kvin Said,

    August 1, 2010 @ 1:50 pm

    How can this be applied when calling the @Future class which makes the webservice callout from a trigger(after insert, before update) as we can’t set the boolean to reflect its a test. Thoughts?

  8. Jeremy ross Said,

    August 4, 2010 @ 3:21 pm

    This would only seem to work if whatever class you’re testing knows to call mainForTest(). What if the call to sample.Main() is several layers deep in the call stack? Now every class involved must “know” about the mainForTest hack. “Intro to Apex Code Test Methods” has the same problem.

    Salesforce gets an F for design on this one.

  9. Nicolas Herment Said,

    May 13, 2011 @ 3:21 pm

    I use a third solution that I like a lot. I’ve created an interface (IHttpCalloutDelegate) and its implementation (HttpCalloutDelegate). Another class (HttpCalloutDelegateMock) also implements IHttpCalloutDelegate but does not actually do any callout.

    IHttpCalloutDelegate defines a simple API with 1 method (‘HttpResponse send()’) and getters/setters (for URL, GET/POST method, httpRequest, …).

    If a class needs to make httpRequests, I create a member variable of type IHttpCalloutDelegate with the default value as being an instance of HttpCalloutDelegate.

    In my tests, I set the IHttpCalloutDelegate variable to be an instance of HttpCalloutDelegateMock just before the test.

    HttpCalloutDelegateMock can be set with a httpRequest (with body, server status, etc.).

    This way, the class can call IHttpCalloutDelegate.send() and not make any callout.

    This allows to have a 100% coverage for all classes (except HttpCalloutDelegate). Moreover:
    – it allows to test different server response codes easily
    – and since I always externalize the test methods in a test class, it allows me to fully encapsulate the internal logic of my classes.

    Just wanted to share my practices 🙂
    – Nick

  10. Dan Said,

    July 7, 2011 @ 9:47 pm

    rather than using a variable you could just use the Test.isRunningTest() static method

  11. fgwarb Said,

    January 4, 2012 @ 8:35 am

    @Dan, FYI, test.IsRunningTest() wasn’t available back in ’09

  12. John Kucera Said,

    February 9, 2012 @ 12:09 am

    I really like this approach for simplicity! It avoids some painful variable passing required by the wiki refactoring approach. Also, I like having a sample responseBody handy for any debugging of code I haven’t touched in a while.

    There’s a few scenarios for me where I need different JSON responses for different test cases to validate error handling or varied responses, so for those methods I extended the idea by adding:

        public static boolean testResponseNum = 1;

    public String mainForTest(Integer num){
            testResponseNum = num;
            return main();
        }

  13. dilleswararao Said,

    June 22, 2012 @ 5:51 am

    if(test.isruningtest)
    {
    httpresponse rs=httpcallsend(req);

    }

  14. dilleswararao Said,

    June 22, 2012 @ 5:53 am

    use the above code to test your code is not correct for this senario

  15. Lee Said,

    August 6, 2012 @ 1:26 am

    Hi,
    I am little bit confusing with writing test cases for Http callouts
    Can you tell me how to write test cases for Htttp callouts here is my code.

    public PageReference Send() {

    HttpRequest req = new HttpRequest();

    req.setEndpoint(‘url’);
    req.setMethod(‘GET’);

    //send the request
    Http http = new Http();
    HttpResponse res = http.send(req);

    //check the response
    if (res.getStatusCode() == 200) {

    //do something//

    } else {
    //do something
    }

    help me???

  16. Aoune Fouad Said,

    November 4, 2012 @ 2:54 pm

    Hi,
    thanks Scott Hemmeter,very useful way!

    We can also use try{}catch(){} order to not stop execution of test method when it comes to the line http.send().

  17. Hannes Ellerbrock Said,

    February 8, 2013 @ 7:09 am

    Hi Scott,

    as this article it still very present in Google, I just wanted to add this info: Salesforce now has built in support to test webservice callouts;
    http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_callouts_wsdl2apex_testing.htm

    Hope this helps others surfing by.

    Best,
    Hannes

RSS feed for comments on this post