-->

How mock REST Request

2020-08-04 10:38发布

问题:

I am using Mockito in JUnit and I have a method making a request to a microservice using RestTemplate.

private static final String REQUESTOR_API_HOST = "http://localhost:8090/requestor/v1/requestors/";

public TokenRequestorPayload getTokenRequestor(Long id) {
    restClient = new RestTemplate();
    return restClient.getForObject(REQUESTOR_API_HOST + id, TokenRequestorPayload.class);
}

This method returns a JSON object which will be deserialize in TokenRequestorPayload class.

When I execute unit tests they fail because mock didn't work and I got a org.springframework.web.client.ResourceAccessException. How can I mock my RestTemplate?

Test

RestTemplate restTemplate = Mockito.spy(RestTemplate.class);
Mockito.doReturn(this.tokenRequestorMockJson()).when(restTemplate).getForObject(Mockito.anyString(), Mockito.eq(TokenRequestorPayload.class));

Error

org.springframework.web.client.ResourceAccessException: I/O error on GET request for "http://localhost:8090/requestor/v1/requestors/1": Connection refused (Connection refused); nested exception is java.net.ConnectException: Connection refused (Connection refused)

回答1:

In your test you are defining behavior for a mock instance of RestTemplate, which is good.

RestTemplate restTemplate = Mockito.spy(RestTemplate.class);

However, the same instance is not used by the Class Under Test which creates a new RestTemplate instance each time and uses that one.

public TokenRequestorPayload getTokenRequestor(Long id, RestTemplate restClient) {
    restClient = new RestTemplate(); // it uses this instance, not the mock
    return restClient.getForObject(REQUESTOR_API_HOST + id, TokenRequestorPayload.class);
}

So you need to find a way to bring the mock instance to the getTokenRequestor() method.

E.g. this can be accomplished by turning restClient into a method parameter:

public TokenRequestorPayload getTokenRequestor(Long id, RestTemplate restClient) {
    return restClient.getForObject(REQUESTOR_API_HOST + id, TokenRequestorPayload.class);
}

and the test might look something like like:

@Test
public void test() {
    RestTemplate restTemplateMock = Mockito.spy(RestTemplate.class);
    Mockito.doReturn(null).when(restTemplateMock).getForObject(Mockito.anyString(), Mockito.eq(TokenRequestorPayload.class));

    // more code

    instance.getTokenRequestor(id, restTemplateMock); // passing in the mock
}


回答2:

Use Spring's mocking support for RestTemplate rather than trying to mock RestTemplate - much nicer:

Create a mock rest service server binding it to your RestTemplate:

MockRestServiceServer mockServer = MockRestServiceServer.createServer(restTemplate);

Record a call to that mock server e.g.

  mockServer.expect(requestTo("some url").andExpect(method(HttpMethod.POST))
                .andRespond(withSuccess("addSuccessResult", MediaType.TEXT_PLAIN));

Then verify the mocks have been called:

mockServer.verify();

See https://objectpartners.com/2013/01/09/rest-client-testing-with-mockrestserviceserver/