23

I have a mock class for a web service that implements WebServiceMock.

With some requests the actual web service being mocked will return a SOAP Fault that results in a System.CalloutException in Apex. E.g. passing in an invalid external ID.

How can I mock the SOAP fault response that results in a CalloutException?

I have code that catches these exceptions that I'd like to test using the mock. I'd prefer not to insert special test cases in the actual code using Test.isRunningTest() to simulate the response.

A sample response from the web service looks like:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
   <s:Body>
      <s:Fault>
         <faultcode>s:Client</faultcode>
         <faultstring xml:lang="en-US">Validation Error(s) occurred during Foo Get.</faultstring>
      </s:Fault>
   </s:Body>
</s:Envelope>

I cannot throw a new instance of System.CalloutException. Doing so gives the error:

Type cannot be constructed: System.CalloutException

I cannot extend System.CalloutExceptions. The compiler gives me the error:

LocalCalloutException: Non-virtual and non-abstract type cannot be extended: System.CalloutException

Usually I would put an instance of the class specified in the responseType parameter into the response parameter dictionary with the key 'response_x'. I tried switching this out with a SOAP fault:

response.put('response_x', '<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body><s:Fault><faultcode>s:Client</faultcode><faultstring xml:lang="en-US">Validation Error(s) occurred during Foo Get.</faultstring></s:Fault></s:Body></s:Envelope>');

This resulted in a (here FooBar_element is the type specified in responseType):

System.TypeException: Collection store exception putting String into MAP<String,FooBar_element>

4
  • You just have to return a regular soap fault, I think. Generate the appropriate xml for a soap fault, and the platform should translate this into a thrown soap fault in the code being tested.
    – sfdcfox
    Jun 24, 2014 at 4:26
  • @sfdcfox Usually I would put an instance of the class specified in the responseType parameter into the response parameter dictionary with the key 'response_x'. Short of throwing an exception that dictionary seems to be the only output mechanism. I'll see if I can put a SOAP fault into it. Jun 24, 2014 at 8:53
  • Can you add FooBar_element.class?
    – Torsen
    Aug 18, 2015 at 12:50
  • @Torsen FooBar_element here will be an arbitrary inner apex class that Wsdl2Apex generated for the SOAP response. It will have members to hold the data, plus a corresponding membername_type_info String[]. There will also be string[] for apex_schema_type_info and field_order_type_info. That's about it. There is no inheritance involved. Aug 18, 2015 at 20:03

3 Answers 3

24
+50

It's possible to scaffold a System.CalloutException by deserializing it into existence or instantiating it with the type. Then you can throw it in your mock:

Adrian Larson notes that as of API v36.0 you can just construct them :-)

@TestVisible class ResponseMock implements WebServiceMock {
    public void doInvoke(
        Object stub,
        Object request,
        Map<String, Object> response,
        String endpoint,
        String soapAction,
        String requestName,
        String responseNs,
        String responseName,
        String responseType
    ) {
        CalloutException e = new CalloutException();
        e.setMessage('Validation Error(s) occurred during Foo Get.');
        throw e;
    }
}

Inject it into your test with the normal HTTP mock method:

Test.setMock(WebServiceMock.class, new ResponseMock());
client.doTheCallout();
3
  • 4
    Brilliant! I wonder what other troublesome classes can be constructed that way? Feb 21, 2016 at 19:22
  • 3
    Wow...I thought it was impossible to throw most standard Exceptions. Mind = blown.
    – Adrian Larson
    Feb 22, 2016 at 0:13
  • It looks like the system is actually allowing the empty constructor, so no need for casting. I wonder if they loosened this up recently?
    – Adrian Larson
    Feb 22, 2016 at 0:17
14

Just extending the findings of @mattandneil. Not trying to steal any thunder, but this is too long to be a comment.

In Creating Custom Exceptions, the documentation still claims:

Since you can’t throw built-in Apex exceptions but can only catch them, you can create custom exceptions to throw in your methods.

Despite this assertion, the compiler now allows built-in Exception types to be constructed. I confirmed through Execute Anonymous with the following snippet:

try
{
    CalloutException e = new CalloutException();
    e.setMessage('This is a constructed exception!');
    throw e;
}
catch (Exception pokemon)
{
    system.debug(pokemon);
}

I went through the whole list and what do you know? The following types can all be constructed and thrown:

  • AsyncException
  • CalloutException
  • DmlException
  • EmailException
  • ExternalObjectException
  • InvalidParameterValueException
  • LimitException (though it still can't be caught)
  • JSONException
  • ListException
  • MathException
  • NoAccessException
  • NoDataFoundException
  • NoSuchElementException
  • NullPointerException
  • QueryException
  • RequiredFeatureMissingException
  • SearchException
  • SecurityException
  • SerializationException
  • SObjectException
  • StringException
  • TypeException
  • VisualforceException
  • XmlException
0

I guess all you need to do is create a custom exception which inherits System.CalloutException. System exception cannot be thrown but their sub classes can.

2
  • 5
    Nice idea. I tried extending System.CalloutException. It won't compile: LocalCalloutException: Non-virtual and non-abstract type cannot be extended: System.CalloutException Jun 24, 2014 at 1:58
  • Sorry, didn't realize that issue. I tried but didn't figure out a way solving that..
    – Lance Shi
    Jun 24, 2014 at 4:47

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .