Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MockMvcClientHttpRequestFactory should implement AsyncClientHttpRequestFactory as well [SPR-15181] #19747

Closed
spring-issuemaster opened this Issue Jan 24, 2017 · 0 comments

Comments

Projects
None yet
2 participants
@spring-issuemaster
Copy link
Collaborator

spring-issuemaster commented Jan 24, 2017

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.