Add AsyncRestTemplate support to client-side MockMvc
Issue: SPR-1822
This commit is contained in:
parent
045d7357d5
commit
9aa53abdf9
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.mock.http.client;
|
||||
|
||||
import org.springframework.core.task.AsyncListenableTaskExecutor;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.client.AsyncClientHttpRequest;
|
||||
import org.springframework.http.client.ClientHttpRequest;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.mock.http.MockHttpOutputMessage;
|
||||
import org.springframework.util.concurrent.ListenableFuture;
|
||||
import org.springframework.util.concurrent.SettableListenableFuture;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
/**
|
||||
* An extension of {@link MockClientHttpRequest} that also implements
|
||||
* {@link AsyncClientHttpRequest} by wraps the response in a "settable" future.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Sam Brannen
|
||||
* @since 3.2
|
||||
*/
|
||||
public class MockAsyncClientHttpRequest extends MockClientHttpRequest implements AsyncClientHttpRequest {
|
||||
|
||||
|
||||
public MockAsyncClientHttpRequest() {
|
||||
}
|
||||
|
||||
public MockAsyncClientHttpRequest(HttpMethod httpMethod, URI uri) {
|
||||
super(httpMethod, uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<ClientHttpResponse> executeAsync() throws IOException {
|
||||
SettableListenableFuture<ClientHttpResponse> future = new SettableListenableFuture<ClientHttpResponse>();
|
||||
future.set(execute());
|
||||
return future;
|
||||
}
|
||||
|
||||
}
|
|
@ -16,17 +16,25 @@
|
|||
package org.springframework.test.web.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URI;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.client.AsyncClientHttpRequest;
|
||||
import org.springframework.http.client.AsyncClientHttpRequestFactory;
|
||||
import org.springframework.http.client.ClientHttpRequest;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.test.web.client.match.MockRestRequestMatchers;
|
||||
import org.springframework.test.web.client.response.MockRestResponseCreators;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.concurrent.ListenableFuture;
|
||||
import org.springframework.util.concurrent.SettableListenableFuture;
|
||||
import org.springframework.web.client.AsyncRestTemplate;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.client.support.RestGatewaySupport;
|
||||
|
||||
|
@ -80,11 +88,9 @@ import org.springframework.web.client.support.RestGatewaySupport;
|
|||
*/
|
||||
public class MockRestServiceServer {
|
||||
|
||||
private final List<RequestMatcherClientHttpRequest> expectedRequests =
|
||||
new LinkedList<RequestMatcherClientHttpRequest>();
|
||||
private final List<RequestMatcherClientHttpRequest> expectedRequests = new LinkedList<RequestMatcherClientHttpRequest>();
|
||||
|
||||
private final List<RequestMatcherClientHttpRequest> actualRequests =
|
||||
new LinkedList<RequestMatcherClientHttpRequest>();
|
||||
private final List<RequestMatcherClientHttpRequest> actualRequests = new LinkedList<RequestMatcherClientHttpRequest>();
|
||||
|
||||
|
||||
/**
|
||||
|
@ -104,12 +110,24 @@ public class MockRestServiceServer {
|
|||
*/
|
||||
public static MockRestServiceServer createServer(RestTemplate restTemplate) {
|
||||
Assert.notNull(restTemplate, "'restTemplate' must not be null");
|
||||
|
||||
MockRestServiceServer mockServer = new MockRestServiceServer();
|
||||
RequestMatcherClientHttpRequestFactory factory = mockServer.new RequestMatcherClientHttpRequestFactory();
|
||||
|
||||
restTemplate.setRequestFactory(factory);
|
||||
return mockServer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@code MockRestServiceServer} and set up the given
|
||||
* {@code AsyRestTemplate} with a mock {@link AsyncClientHttpRequestFactory}.
|
||||
*
|
||||
* @param asyncRestTemplate the AsyncRestTemplate to set up for mock testing
|
||||
* @return the created mock server
|
||||
*/
|
||||
public static MockRestServiceServer createServer(AsyncRestTemplate asyncRestTemplate) {
|
||||
Assert.notNull(asyncRestTemplate, "'asyncRestTemplate' must not be null");
|
||||
MockRestServiceServer mockServer = new MockRestServiceServer();
|
||||
RequestMatcherClientHttpRequestFactory factory = mockServer.new RequestMatcherClientHttpRequestFactory();
|
||||
asyncRestTemplate.setAsyncRequestFactory(factory);
|
||||
return mockServer;
|
||||
}
|
||||
|
||||
|
@ -171,7 +189,6 @@ public class MockRestServiceServer {
|
|||
sb.append(request.toString()).append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
@ -180,12 +197,22 @@ public class MockRestServiceServer {
|
|||
* Mock ClientHttpRequestFactory that creates requests by iterating
|
||||
* over the list of expected {@link RequestMatcherClientHttpRequest}'s.
|
||||
*/
|
||||
private class RequestMatcherClientHttpRequestFactory implements ClientHttpRequestFactory {
|
||||
private class RequestMatcherClientHttpRequestFactory
|
||||
implements ClientHttpRequestFactory, AsyncClientHttpRequestFactory {
|
||||
|
||||
private Iterator<RequestMatcherClientHttpRequest> requestIterator;
|
||||
|
||||
@Override
|
||||
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
|
||||
return createRequestInternal(uri, httpMethod);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AsyncClientHttpRequest createAsyncRequest(URI uri, HttpMethod httpMethod) throws IOException {
|
||||
return createRequestInternal(uri, httpMethod);
|
||||
}
|
||||
|
||||
private RequestMatcherClientHttpRequest createRequestInternal(URI uri, HttpMethod httpMethod) {
|
||||
Assert.notNull(uri, "'uri' must not be null");
|
||||
Assert.notNull(httpMethod, "'httpMethod' must not be null");
|
||||
|
||||
|
@ -201,9 +228,8 @@ public class MockRestServiceServer {
|
|||
request.setMethod(httpMethod);
|
||||
|
||||
MockRestServiceServer.this.actualRequests.add(request);
|
||||
|
||||
return request;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@ import java.util.LinkedList;
|
|||
import java.util.List;
|
||||
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.mock.http.client.MockAsyncClientHttpRequest;
|
||||
import org.springframework.mock.http.client.MockClientHttpRequest;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
@ -33,7 +34,7 @@ import org.springframework.util.Assert;
|
|||
* @author Rossen Stoyanchev
|
||||
* @since 3.2
|
||||
*/
|
||||
class RequestMatcherClientHttpRequest extends MockClientHttpRequest implements ResponseActions {
|
||||
class RequestMatcherClientHttpRequest extends MockAsyncClientHttpRequest implements ResponseActions {
|
||||
|
||||
private final List<RequestMatcher> requestMatchers = new LinkedList<RequestMatcher>();
|
||||
|
||||
|
|
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.test.web.client.samples;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.test.web.Person;
|
||||
import org.springframework.test.web.client.MockRestServiceServer;
|
||||
import org.springframework.util.concurrent.ListenableFuture;
|
||||
import org.springframework.web.client.AsyncRestTemplate;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.springframework.test.web.client.match.MockRestRequestMatchers.method;
|
||||
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
|
||||
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
|
||||
|
||||
/**
|
||||
* Examples to demonstrate writing client-side REST tests with Spring MVC Test.
|
||||
* While the tests in this class invoke the RestTemplate directly, in actual
|
||||
* tests the RestTemplate may likely be invoked indirectly, i.e. through client
|
||||
* code.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class SampleAsyncTests {
|
||||
|
||||
private MockRestServiceServer mockServer;
|
||||
|
||||
private AsyncRestTemplate restTemplate;
|
||||
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.restTemplate = new AsyncRestTemplate();
|
||||
this.mockServer = MockRestServiceServer.createServer(this.restTemplate);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void performGet() throws Exception {
|
||||
|
||||
String responseBody = "{\"name\" : \"Ludwig van Beethoven\", \"someDouble\" : \"1.6035\"}";
|
||||
|
||||
this.mockServer.expect(requestTo("/composers/42")).andExpect(method(HttpMethod.GET))
|
||||
.andRespond(withSuccess(responseBody, MediaType.APPLICATION_JSON));
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
ListenableFuture<ResponseEntity<Person>> ludwig = restTemplate.getForEntity("/composers/{id}", Person.class, 42);
|
||||
|
||||
// We are only validating the request. The response is mocked out.
|
||||
// person.getName().equals("Ludwig van Beethoven")
|
||||
// person.getDouble().equals(1.6035)
|
||||
|
||||
this.mockServer.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void performGetAsync() throws Exception {
|
||||
|
||||
String responseBody = "{\"name\" : \"Ludwig van Beethoven\", \"someDouble\" : \"1.6035\"}";
|
||||
|
||||
this.mockServer.expect(requestTo("/composers/42")).andExpect(method(HttpMethod.GET))
|
||||
.andRespond(withSuccess(responseBody, MediaType.APPLICATION_JSON));
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
ListenableFuture<ResponseEntity<Person>> ludwig = restTemplate.getForEntity("/composers/{id}", Person.class, 42);
|
||||
|
||||
// person.getName().equals("Ludwig van Beethoven")
|
||||
// person.getDouble().equals(1.6035)
|
||||
|
||||
this.mockServer.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void performGetWithResponseBodyFromFile() throws Exception {
|
||||
|
||||
Resource responseBody = new ClassPathResource("ludwig.json", this.getClass());
|
||||
|
||||
this.mockServer.expect(requestTo("/composers/42")).andExpect(method(HttpMethod.GET))
|
||||
.andRespond(withSuccess(responseBody, MediaType.APPLICATION_JSON));
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
ListenableFuture<ResponseEntity<Person>> ludwig = restTemplate.getForEntity("/composers/{id}", Person.class, 42);
|
||||
|
||||
// hotel.getId() == 42
|
||||
// hotel.getName().equals("Holiday Inn")
|
||||
|
||||
this.mockServer.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verify() {
|
||||
|
||||
this.mockServer.expect(requestTo("/number")).andExpect(method(HttpMethod.GET))
|
||||
.andRespond(withSuccess("1", MediaType.TEXT_PLAIN));
|
||||
|
||||
this.mockServer.expect(requestTo("/number")).andExpect(method(HttpMethod.GET))
|
||||
.andRespond(withSuccess("2", MediaType.TEXT_PLAIN));
|
||||
|
||||
this.mockServer.expect(requestTo("/number")).andExpect(method(HttpMethod.GET))
|
||||
.andRespond(withSuccess("4", MediaType.TEXT_PLAIN));
|
||||
|
||||
this.mockServer.expect(requestTo("/number")).andExpect(method(HttpMethod.GET))
|
||||
.andRespond(withSuccess("8", MediaType.TEXT_PLAIN));
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
ListenableFuture<ResponseEntity<String>> result = this.restTemplate.getForEntity("/number", String.class);
|
||||
// result == "1"
|
||||
|
||||
result = this.restTemplate.getForEntity("/number", String.class);
|
||||
// result == "2"
|
||||
|
||||
try {
|
||||
this.mockServer.verify();
|
||||
}
|
||||
catch (AssertionError error) {
|
||||
assertTrue(error.getMessage(), error.getMessage().contains("2 out of 4 were executed"));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -61,8 +61,9 @@ public class SampleTests {
|
|||
@SuppressWarnings("unused")
|
||||
Person ludwig = restTemplate.getForObject("/composers/{id}", Person.class, 42);
|
||||
|
||||
// person.getName().equals("Ludwig van Beethoven")
|
||||
// person.getDouble().equals(1.6035)
|
||||
// We are only validating the request. The response is mocked out.
|
||||
// hotel.getId() == 42
|
||||
// hotel.getName().equals("Holiday Inn")
|
||||
|
||||
this.mockServer.verify();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue