[SalesForce] Mocking a webservice CalloutException for a SOAP Fault

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>

Best Answer

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();