How to mock response of an external API called from within an internal API integration test

Viewed 5463

I am writing integration tests for an application that uses an internal REST API to perform an action; all Java.
In the API, there is a POST method that calls upon an external API. In the test, I need to send a request to my API to perform an action.
The problem is, I do not want to send real requests to the external API when running the integration tests.
How can I mock the response of the external API call?
Is there a way I can still send a POST request to my API (mocked or otherwise) within my test, but use a mocked response for the external call performed in the Java POST method?

1 Answers

I've done this as follows: create a service layer that makes the API calls to external services. I built mine on Spring's RestTemplate but you could use whatever library to make calls. So it'll have methods like get() or post().

Then I use Postman to perform some requests against the external API and save the responses in files and add these files to my project.

Finally, in my tests, I mock out the calls to my little API service layer so that instead of going to the external API, it reads from the test files I saved previously. This runs the code under test with known payloads that came from the external API, but without requiring a connection to it during the test, and which won't change until I update the responses in the files myself.

I use EasyMock but any mocking library will work. Here's an example of what a test looks like.

    @Test
    public void test_addPhoneToExternalService() {
        // Mock my real API service.
        ApiService mockApiService = EasyMock.createMock(ApiService.class);
        // Construct my service under test using my mock instead of the real one.
        ServiceUnderTest serviceUnderTest = new ServiceUnderTest(mockApiService);
        // Expect the body of the POST request to look like this.
        Map<String, Object> requestBody = new HashMap<String, Object>() {{
            put("lists", 0);
            put("phone", "800-555-1212");
            put("firstName", "firstName");
            put("lastName", "lastName");
        }};
        // Read the response that I manually saved as json.
        JsonNode expectedResponse = Utils.readJson("response.json");
        expect(mockApiService.post(anyObject(),
                eq("https://rest.someservice.com/api/contacts"), eq(serialize(requestBody))))
                .andReturn(expectedResponse);
        EasyMock.replay(mockApiService);
        // Call the code under test. It ingests the response 
        // provided by the API service, which is now mocked, 
        // and performs some operations, then returns a value 
        // based on what the response contained (for example, 
        // "{\"id\":42}").
        long id = serviceUnderTest.addPhone("firstName", "lastName", "800-555-1212");
        Assert.assertThat(id, is(42L));
        EasyMock.verify(mockApiService);
    }

Hope that helps!

Related