Create Unit Testing for Asynchronous Callouts

You can use the ExternalServiceTest method to mock callback and asynchronous responses.

Required Editions

Available in: Lightning Experience
Available in: Enterprise, Performance, Unlimited, and Developer Editions

The code examples in this topic are based on the fictitious schema in Example API Specification With Callback Operation.

This snippet shows an example unit test for the Acme Mortgage service. To test external services with callbacks in Apex, the Acme Mortgage callout mock class mocks the submit-mortgage application request and asserts the expected request payload. The test class triggers the mocked external service’s callback response through an instance of test External Service methods.

global class AcmeMortgageHttpCalloutMock implements HttpCalloutMock {
    private String callbackUrl;
    
    // Mock asynchronous response
    public void callBack() {
        HttpRequest request = new HttpRequest();
        request.setEndpoint(callbackUrl);
        request.setMethod('POST');
        request.setHeader('Content-Type', 'application/json');
        request.setBody('{' + 
                            '"applicationNumber": "900-X",' + 
                            '"status": "Approved",' +
                            '"approvedAmount": 10000' + 
                        '}');
                        
        HttpResponse response = Test.getExternalService().sendCallback(request);
        Assert.areEqual(200, response.getStatusCode());
    }
          
    // Mock initial response
    global HttpResponse respond(HttpRequest request) {
        if (request.getMethod().equals('POST') && request.getEndpoint().equals('callout:AcmeMortgageCredential/applications')) {
            return respondSubmitApplication(request);
        }
        ...
        System.assert(false, 'Request method and/or endpoint not valid: ' + request.getMethod() + ' ' + request.getEndpoint());
        return null;
    }
    
    private HttpResponse respondSubmitApplication(HttpRequest request) {
        // Retrieve the callback URL from request
        ExternalService.AcmeMortgage_SubmitApplication_IN_Body body = 
(ExternalService.AcmeMortgage_SubmitApplication_IN_Body) 
                                     Json.deserialize(request.getBody(), 
(ExternalService.AcmeMortgage_SubmitApplication_IN_Body.class);
        callbackUrl = body.callbackUrlForOutcomes.approved;
        
        HttpResponse response = new HttpResponse();
        response.setHeader('Content-Type', 'application/json');
        response.setBody('{"applicationNumber": "900-X"}');
        response.setStatusCode(201);
        return response;
    }
}

The test class sets up the callout mock, sends the mortgage application request, and then mocks the callback with the mortgage application outcome:

@IsTest
public class AcmeMortgageApplicationTest {
    @IsTest
    static void testSubmitApplicationWithApprovalCallback() {
        AcmeMortgageHttpCalloutMock calloutMock = new AcmeMortgageHttpCalloutMock();
        Test.setMock(HttpCalloutMock.class, calloutMock);
        Test.startTest();
        
        ExternalService.AcmeMortgage acme = new ...();
        ...acme.SubmitApplication(...);
        
        // Call back with mock responses
        calloutMock.callBack();
        // Send all registered callback
        Test.stopTest();
        
        // Assert expected end result - e.g approved mortgage object created
        ...
    }
}