Blog
Thoughts on technology and innovation
Luke Freeland, Senior Technical Consultant Recently, the Nimble AMS feature I was developing in Salesforce required one or more web service callouts. Naturally, this code has to be tested. See the "Original Test Code" at the end if desired. After building up the test suite, the TestWebServiceMockImpl became rather lengthy with many nested if elses. This is unfortunately necessary because only one mock class can be set per test method and some test methods invoke multiple web service callouts. Reviewing the code made me think… ewwww, this stinks with a nasty code smell…. How can this be improved? Answer: A mini framework. Apex WebService Callout Test Mini FrameworkThe solution is to create a single mock class that delegates the mock request to one or more responder classes which create the response object for the given request. Let’s see some code. We start by creating a Responder interface that is responsible for creating response objects for a given request. public interface ITestWebServiceResponder { object createResponse( Object stub, Object request, String endpoint, String soapAction, String requestName, String responseNS, String responseName, String responseType); } The Executor class is the only class that implements WebServiceMock and is used throughout our test suite. It’s responsible for executing the responders to generate the appropriate response for any given web service callout. global class TestWebServiceMockExecutor implements WebServiceMock { // Use simple lookup table to grab the responder indexed by request name Map<String, ITestWebServiceResponder> responders = new Map<String, ITestWebServiceResponder>{ ‘Request_a’, TestWebServiceAResponder, ‘Request_b’, TestWebServiceBResponder, // other responders here }; global TestWebServiceMockExecutor() { } // Use constructor dependency injection to allow callers to add additional // responders or replace existing ones. global TestWebServiceMockExecutor( Map<String, ITestWebServiceResponder> otherResponders){ responders.putAll(otherResponders); } global void doInvoke( Object stub, Object request, Map<String, Object> response, String endpoint, String soapAction, String requestName, String responseNS, String responseName, String responseType) { // Grab the responder to use by request name. ITestWebServiceResponder responder = responders.get(requestName); // Responder creates the web service response. Object response_x = responder.createResponse( stub, request, endpoint, soapAction, requestName, responseNS, responseName, responseType ); // Put the response in the response object response.put(‘response_x’, response_x); } // Responder class can be nested within the test class or be a separate class in the org. @isTest public class TestWebServiceAResponder implements ITestWebServiceResponder { public object createResponse( stub, request, endpoint, soapAction, requestName, responseNS, responseName, responseType){ ResponseElement1 re1 = new ResponseElement1(); re1.property1 = some_value; return re1; } } Now, our test classes simply use the Executor class for the mock class and we’re all done. @isTest private class TestWebServiceCallout { static testmethod void webServiceCallout1Test(){ Test.setMock(WebServiceMock.class, new TestWebServiceMockExecutor()); // Test code that invokes webservice callout 1 } static testmethod void webServiceCallout2Test(){ Test.setMock(WebServiceMock.class, new TestWebServiceMockExecutor()); // Test code that invokes webservice callout 1 and 2. } } Advantages
DisadvantagesPlease share if you find some. Happy Coding, Luke Original Test Code (Ewwww)global class TestWebServiceMockImpl implements WebServiceMock {
global void doInvoke( Object stub, Object request, Map<String, Object> response, String endpoint, String soapAction, String requestName, String responseNS, String responseName, String responseType) { if (requestName == ‘request_a’){ ResponseElement1 re1 = new ResponseElement1(); re1.property1 = some_value; response.put(‘response_x’, re1); } else if (requestName == ‘request_b’){ ResonseElement2 re2 = new ResponseElement2(); re2.property1 = some_value; re2.property2 = some_other_value; response.put(‘response_x’, re2); } // other responses handled. } } And my test class looked like: @isTest private class TestWebServiceCallout { static testmethod void webServiceCallout1Test(){ Test.setMock(WebServiceMock.class, new TestWebServiceMockImpl()); // Test code that invokes webservice callout 1 } static testmethod void webServiceCallout2Test(){ Test.setMock(WebServiceMock.class, new TestWebServiceMockImpl()); // Test code that invokes webservice callout 1 and 2. } }
Comments
|
Products |
Resources |
Get Help |
Company |
NimbleUser is now part of the Community Brands family.
|
© 2018 All Rights Reserved NimbleUser
Terms and Conditions |