+ Start a Discussion
Ken_KoellnerKen_Koellner 

Callouts from classes invoked from runtests

I imported some WSDL to get stubs and wrote a class that successfully class them when testing interactively.

 

I'm now writing Apex tests and my first test that calls Apex methods that will eventually get into methods that do callouts is failing with an exception. 

 

Are the any specific techniques that are needed to get Apex test methods to run when callouts are invoked?

 

 

Ken_KoellnerKen_Koellner

I printed out the cause of the exception --

 

System.TypeException: Methods defined as TestMethod do not support Web service callouts, test skipped

 

So obviosly you can't get callouts to work.

 

Is there some technique to work-around this and simulate callouts in testing?


Ken_KoellnerKen_Koellner

I found this paper -- http://wiki.developerforce.com/index.php/An_Introduction_to_Apex_Code_Test_Methods

 

But I think the technique there doesn't help.

 

I need to test several layers of business logic that sit on top of methods that are wrappers for callouts.  The business logic relies on those methods being there. 

 

I don't know if there's any way to detect test mode and conditionally not do the callout in the web service and instead, simulate data.

 

 

sforce2009sforce2009

Yes You can achieve this.

set a property isTestMode in your class like

public boolean isTestMode

{get;set;}

check like this wherever you are using callouts

if(!isTest)//if isTest is false

//callout process

else

//nothing

 

set isTest to true in your test method. This will make sure your callout method does not get called.

 

Cheers

Ken_KoellnerKen_Koellner

That seems like a viable technique.  We put our tests in separately classes but that doesn't meed it could be public and then in the test class do --

 

  MyClassThatDoesCallOut.isTestMode = true;

 

 

What I ended up doing is using certain key values to indicate tests.  All the callouts I have look up data from other systems.  I have wrappers around my callouts anyway and I put in tests for key values beginning with "ZZTEST".  If the key is like that, it transfer to a simulate method.  I can use different key values to get different results like ZZTESTNF will end up with a not found.  ZZTESTZ3 will return three results, etc.

 

What would be cool is if Apex had function pointers, then in test mode, rather than just setting a flag, I'd pass in a pointer to method that does the simulate, the wrapper could then call back to the test class.

 

incuGuSincuGuS

Hey,

 

Be sure to check out http://wiki.developerforce.com/index.php/An_Introduction_to_Apex_Code_Test_Methods page, it has a lot of info and some good practices on how to test Callouts.

 

Hope this helps,

Gaston.

Ken_KoellnerKen_Koellner

I already sited that paper above.  I found the case about callouts in the paper pretty much worthless.

 

 

incuGuSincuGuS

Its not worthless, if you follow the pattern there

 

public void call(){   
//now need to make web service callout

//First, build the http request
Http h = new Http();
HttpRequest req = buildWebServiceRequest();

//Second, invoke web service call
HttpResponse res = invokeWebService(h, req);

//Last, handling the response
handleWebServiceResponse(res);
}

 

 

 

You can create test methods that do the following:

 

 

static testMethod void testWebService(){
//First, build the http request
HttpRequest req = buildWebServiceRequest();

//NOTE - WE DO NOT EXECUTE THE METHOD, invokeWebService.

//Now, since we can't execute the actual web service,
//write apex code to build a sample HttpResponse object

In here create a method that simulates the data returned from the webservice, if its a xml create a sample xml string here

HttpResponse res = new HttpResponse();
//Apply test data and attributes to the HttpResponse object as needed
handleWebServiceResponse(res);

}

 

So you can simulate the data and test the creation of the request and the processing.

You cant do callouts in test methods, so this is the best way to cover as much code and methods as possible.

 

Ive tested all kinds of callouts following this method, and inside the testmethod having a String with the xml/whatever returned there.

 

Im sorry if im not following you, but this seems to be the way you need to test your methods, if not could you elaborate more on why this does not apply?

 

Thanks,

Gaston.

 

Ken_KoellnerKen_Koellner

This is is not covering the logic that deals with the callout requests.  The issue is covering all the business logic that relies on the callout.  I'm using webService callouts so there's no prep and response code to cover.  Everything having to do with the call out is a single line (except for getting the stub).

 

The code is like --

 

 businessMethodA calls BusinessMethodB calls BusinessMethodC.

 

BusinessMethod C does the call out,.

 

I have to get coverated on the A, B, and C methods.  The testmethod is going to call businessMethodA.  Were the call out would take place in method C, something has to simulate the callout so that methods A, B, and C have data to operate on.

 

The techniqe in the manual has the callout in the test method itself and comments it out.  that doesn't do any good for all the layers on top that may use the callout.

 

sforce2009sforce2009

I am sorry if it is a lengthy response. All you have to do is divide your call outs to buildrequest, invokerequest, handlerequest and handle your code..

I have done this before for Authorize.net web service call outs..

 

Inside your class:

 

public HttpRequest buildWebServiceRequest1()
  {

      HttpRequest req = new HttpRequest();
      req.setEndpoint(SOAPEndPoint);
       req.setMethod('POST');
       req.setHeader('Content-Type', 'text/xml; charset=utf-8');
       req.setHeader('SOAPAction', 'https://api.authorize.net/soap/v1/CreateCustomerProfile');//60110000000000122009-12
        string b =   'your soap body here';
        
        req.setBody(b);
      return req;
  }
  public String invokeWebService1(Http h, HttpRequest req)
  {
     
       if(isTest != null && isTest)
         return '<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"></soap:Envelope>';//dummy xml response here
       else
       {
         //Invoke Web Service
         HttpResponse res = h.send(req);
         
         return res.getBody();
       }
       return '';
  }
  
  public void handleWebServiceResponse1(String res)
  {

       XmlStreamReader reader = new XmlStreamReader(res);
    	//Call your next method here...
	 Http http = new Http();
      String SoapBody = invokeWebService2(http, buildWebServiceRequest2());//similar to invokeWebservice1
      handleWebServiceResponse2(SoapBody);
  }

 

 

Test method:

 

static testMethod void myUnitTest() 
    {
        
        
        Trekbin_CIM_Billing objCIM = new Trekbin_CIM_Billing();
        
        objCIM.isTest = true;
        
          HttpRequest req = objCIM.buildWebServiceRequest1();
         Http h = new Http();
          string res = objCIM.invokeWebService1(h, req); 
      objCIM.handleWebServiceResponse1('<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"></soap:Envelope>');//Dummy Response
        
          req = objCIM.buildWebServiceRequest2();
          h = new Http();
          res = objCIM.invokeWebService2(h, req); 
      objCIM.handleWebServiceResponse2('<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"></soap:Envelope>');//Dummy Response
        
  }

Let me know if this is useful at all to you.

 

Thanks

 

 

WilmerWilmer

Hi Ken,

 

To simulate your callout in your test method you should first of all, adjust your apex class (where you execute the callout) taking care of this:

 

1. Create a public boolean variable in your class that works like a flag, let this class being able to know when to run the callout and when not.

2. Create a public "Response" variable to set a simulated response.

3. Include logic to your class that works according to the flag of point 1.

 

Later, in your testMethod invoke the RESPONSE (of your apex class) and set the kind of response you need to test, set the flag value and call the apex class method that executes the callout.

 

See the following example:

Apex Class (the one that runs the callout)

 

global class SyncClass_snc {
	public static boolean blnIsTest = false;			// Controls when a callout is invoked from a TestClass to avoid restriction 
	public static MyCreatedFromWSDLClass.RESPONSE MyResponseTest; // Loads the object related to the response from the callout
	
	public static void SendInfo(string strFirstName, string strLastName){
		try{
			// Here comes your custom code...
			
			// Builds the object to send.
			MyCreatedFromWSDLClass.ServiceSoap11 MyService = new MyCreatedFromWSDLClass.ServiceSoap11(); 
			MyCreatedFromWSDLClass.BASIC_DATA BasicDataInfo = new MyCreatedFromWSDLClass.BASIC_DATA();
			MyCreatedFromWSDLClass.RESPONSE MyRealResponse = new MyCreatedFromWSDLClass.RESPONSE();
			
			// Prepares data
			BasicDataInfo.FirstName = strFirstName;
			BasicDataInfo.LastName = strLastName;
			
			if(!blnIsTest){ // If it is not a test, runs the real callout
				MyRealResponse = MyService.RunCallout(BasicDataInfo);
			}else{
				MyRealResponse = MyResponseTest; // Passes by value the callout simulated response
			}
			// Here comes the logic that processes the response...
			
		}catch(System.Exception e){
			system.debug('***** The following Error has been catched: Type: '+e.getTypeName()+'. Message: '+e.getMessage();
		}
	}
}

 

 

Apex TestMethod

 

@isTest
private class SyncTest_tst {
    
	static testMethod void test1() {
		// Sets the flag to TRUE to avoid real callouts
    	SyncClass_snc.blnIsTest = true;
		
		// Sets the custom Response
		SyncClass_snc.MyResponseTest = = new MyCreatedFromWSDLClass.RESPONSE();
		SyncClass_snc.MyResponseTest.SUCCESS_OPERATION = true; // Sets an affirmative response
		
		test.startTest();
			// Calls the method that runs the callout
			SyncClass_snc.SendInfo('John', 'Smith');
			
			// Now, tries with a controlled error
			SyncClass_snc.MyResponseTest = = new MyCreatedFromWSDLClass.RESPONSE();
			SyncClass_snc.MyResponseTest.SUCCESS_OPERATION = false; // Sets an negative response
			
			// Calls the method that runs the callout
			SyncClass_snc.SendInfo('Sarah', 'Brookes');
			
		test.stopTest();
	}
}

 

I hope this info and example help you.

 

 

Regards,

 

 

Wilmer

 

 

Ken_KoellnerKen_Koellner

Wilmer, that's pretty much what I did except I keyed it off an input parameter.  Certain values can't exist in real datal so the testmethod passes in values like ZZTEST123 which are detect in the low-level method an shunted to a simulate method.