1

In my application, I have external third party API requests. I want to test my application with mocking this API requests.

My Service Class:

String API_URL = "https://external.com/v1/%s";
private CloseableHttpClient httpClient;
public Result executeRequest(String apiVersion, String subUrl, HttpMethod httpMethod)
{
    try
    {
        HttpRequestBase httpRequest;
        String url = String.format(API_URL, subUrl);
        if (httpMethod.equals(HttpMethod.GET))
        {
            httpRequest = new HttpGet(url);
        }
        else if (httpMethod.equals(HttpMethod.POST))
        {
            httpRequest = new HttpPost(url);
            ((HttpPost) httpRequest).setEntity(new StringEntity(requestBody, "UTF-8"));
        }
        ...
        headers.forEach(httpRequest::setHeader);
        HttpResponse response = httpClient.execute(httpRequest);
    }
    catch (IOException e)
    {
        logger.error("IO Error: {}", e.getMessage());
        return handleExceptions(e);
    }
}

To sum up for service class, requests can be get, post, delete, put. And this requests will be processed with headers or body parts. Then will be handled as http request.

My test class:

@SpringBootTest
@ActiveProfiles("test")
@RunWith(SpringRunner.class)
public class ServiceTest
{

    private static final String API_URL = "https://external.com/v1";

    @Autowired
    private Service service;

    @Autowired
    private RestTemplate restTemplate;

    private MockRestServiceServer mockServer;

     @Before
     public void init()
      {
        mockServer = MockRestServiceServer.createServer(restTemplate);
      }

    @Test
    public void getResult_successfully()
    {
        Result result = new Result();
        mockServer.expect(ExpectedCount.once(),
                requestTo("/subUrl"))
                .andExpect(method(HttpMethod.GET))
                .andRespond(withStatus(HttpStatus.OK)
                        .contentType(MediaType.APPLICATION_JSON)
                        .body(gson.toJson(result))
                );

        Result returnResult = service.executeRequest("/subUrl", GET);

        mockServer.verify();
        assertThat(returnResult).isEqualTo(result);
    }
}

When I implement it like above, mocking doesn't work. Any suggestion?

I know in here, MockRestServiceServer work only with restTemplate requests but I wonder there is a way to handle it with httpClient?

Note: I hope code snippets will be enough to understand the code in overall.

Sha
  • 921
  • 17
  • 46
  • Might [help](https://stackoverflow.com/questions/57328602/mockrestserviceserver-how-to-mock-a-post-call-with-a-body). What's not "working" exactly? is the assert failing? Request not coming through at all? – Tony May 31 '20 at 16:38
  • @Tony MockRestServiceServer works only with restTemplate requests but I wonder there is a way to handle it with httpClient? – Sha May 31 '20 at 16:47
  • ah so explicitly use httpClient? Not sure. The only thing I can offer is trying `MockMvcRequestBuilders`. – Tony May 31 '20 at 16:56
  • @Tony, this is not a controller test. Question is related with embedded-test-server. – Sha May 31 '20 at 17:18

1 Answers1

0

I've used mock-server for this. You can hard-code the responses (as in the example below) or pass the request through to an actual server.

private static ClientAndServer mockServer;

@BeforeClass
public static void startServer() {
    mockServer = ClientAndServer.startClientAndServer(1080);
}

@AfterClass
public static void stopServer() { 
    mockServer.stop();
    mockServer = null;
}

@Test
public void getShouldWork() throws URISyntaxException, IOException {
    final String responseBody = "some_response_body";
    final String respHdrName = "some_header";
    final String respHdrVal = "some value";
    
    mockServer.when(
            request()
                .withMethod("GET")
                .withPath("/some/url")
                .withQueryStringParameter("id", "9"),
            Times.exactly(1)
        )
        .respond(
            response()
                .withBody(responseBody)
                .withHeader(respHdrName, respHdrVal)
        );

    
    CloseableHttpClient httpClient = HttpClientBuilder.create()
            .build();
    
    HttpGet getRequest = new HttpGet("http://localhost:" + mockServer.getPort() 
            + "/some/url?id=9");
    CloseableHttpResponse resp = httpClient.execute(getRequest);
    try (InputStreamReader isr = new InputStreamReader(resp.getEntity().getContent());
            BufferedReader br = new BufferedReader(isr);) {
        assertThat(br.readLine(), is(responseBody));
        while (br.readLine() != null) {
            // consume content...
        }
    }
    assertThat(
            resp.getFirstHeader(respHdrName), 
            is(notNullValue()));
    assertThat(
            resp.getFirstHeader(respHdrName).getValue(), 
            is(respHdrVal));
}

The relevant imports for the above are:

import static org.mockserver.model.HttpRequest.request;
import static org.mockserver.model.HttpResponse.response;
import org.mockserver.integration.ClientAndServer;
import org.mockserver.matchers.Times;

And finally, the maven coordinates of the necessary libs:

    <dependency>
        <groupId>org.mock-server</groupId>
        <artifactId>mockserver-netty</artifactId>
        <version>5.11.2</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.mock-server</groupId>
        <artifactId>mockserver-client-java</artifactId>
        <version>5.11.2</version>
        <scope>test</scope>
    </dependency>
Tim Perry
  • 84
  • 1
  • 11