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.
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.
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.
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.
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 […]
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?
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.
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?
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.
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
Dan Said,
July 7, 2011 @ 9:47 pm
rather than using a variable you could just use the Test.isRunningTest() static method
fgwarb Said,
January 4, 2012 @ 8:35 am
@Dan, FYI, test.IsRunningTest() wasn’t available back in ’09
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();
}
dilleswararao Said,
June 22, 2012 @ 5:51 am
if(test.isruningtest)
{
httpresponse rs=httpcallsend(req);
}
dilleswararao Said,
June 22, 2012 @ 5:53 am
use the above code to test your code is not correct for this senario
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???
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().
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