Closed
Description
Artem Bilan opened SPR-15181 and commented
The use-case is like:
AsyncRestTemplate asyncRestTemplate = new AsyncRestTemplate(new MockMvcClientHttpRequestFactory(this.mockMvc));
new DirectFieldAccessor(this.serviceAsyncGatewayHandler)
.setPropertyValue("asyncRestTemplate", asyncRestTemplate);
this.mockMvc.perform(...);
To check the component based on the AsyncRestTemplate
against some MVC environment.
But MockMvcClientHttpRequestFactory
doesn't implement AsyncClientHttpRequestFactory
and looks like there is no any other alternative unless home-cooked solution, for example:
private final class MockMvcAsyncClientHttpRequestFactory extends MockMvcClientHttpRequestFactory
implements AsyncClientHttpRequestFactory {
MockMvcAsyncClientHttpRequestFactory(MockMvc mockMvc) {
super(mockMvc);
}
@Override
public AsyncClientHttpRequest createAsyncRequest(URI uri, HttpMethod httpMethod) throws IOException {
final ClientHttpRequest syncDelegate = createRequest(uri, httpMethod);
return new MockAsyncClientHttpRequest(httpMethod, uri) {
@Override
public HttpHeaders getHeaders() {
return syncDelegate.getHeaders();
}
@Override
public OutputStream getBody() throws IOException {
return syncDelegate.getBody();
}
@Override
protected ClientHttpResponse executeInternal() throws IOException {
return syncDelegate.execute();
}
};
}
}
Not sure that it will work for all use-case, but at least it meets my requirements even with the Basic Authentication header.
For out-of-the-box solution I suggest this modification to the MockMvcClientHttpRequestFactory
:
public class MockMvcClientHttpRequestFactory implements ClientHttpRequestFactory, AsyncClientHttpRequestFactory {
private final MockMvc mockMvc;
public MockMvcClientHttpRequestFactory(MockMvc mockMvc) {
Assert.notNull(mockMvc, "MockMvc must not be null");
this.mockMvc = mockMvc;
}
@Override
public ClientHttpRequest createRequest(final URI uri, final HttpMethod httpMethod) throws IOException {
return new MockClientHttpRequest(httpMethod, uri) {
@Override
public ClientHttpResponse executeInternal() throws IOException {
return doExecute(httpMethod, uri.toString(), getHeaders(), getBodyAsBytes());
}
};
}
@Override
public AsyncClientHttpRequest createAsyncRequest(URI uri, HttpMethod httpMethod) throws IOException {
return new MockAsyncClientHttpRequest(httpMethod, uri) {
@Override
protected ClientHttpResponse executeInternal() throws IOException {
return doExecute(httpMethod, uri.toString(), getHeaders(), getBodyAsBytes());
}
};
}
private ClientHttpResponse doExecute(HttpMethod httpMethod, String uri, HttpHeaders httpHeaders, byte[] body) {
try {
MockHttpServletRequestBuilder requestBuilder = request(httpMethod, uri);
requestBuilder.content(body);
requestBuilder.headers(httpHeaders);
MvcResult mvcResult = MockMvcClientHttpRequestFactory.this.mockMvc.perform(requestBuilder).andReturn();
MockHttpServletResponse servletResponse = mvcResult.getResponse();
HttpStatus status = HttpStatus.valueOf(servletResponse.getStatus());
byte[] reply = servletResponse.getContentAsByteArray();
HttpHeaders headers = getResponseHeaders(servletResponse);
MockClientHttpResponse clientResponse = new MockClientHttpResponse(reply, status);
clientResponse.getHeaders().putAll(headers);
return clientResponse;
}
catch (Exception ex) {
byte[] reply = ex.toString().getBytes(StandardCharsets.UTF_8);
return new MockClientHttpResponse(reply, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
private HttpHeaders getResponseHeaders(MockHttpServletResponse response) {
HttpHeaders headers = new HttpHeaders();
for (String name : response.getHeaderNames()) {
List<String> values = response.getHeaders(name);
for (String value : values) {
headers.add(name, value);
}
}
return headers;
}
}
Would be glad to see the fix in the 5.0
, we have a PR in Spring Integration which now doesn't use MockMvc
because of this limitation.
Thanks
Referenced from: commits 4da4f2b