Browse Source

Add AsyncRestTemplate support to client-side MockMvc

Issue: SPR-1822
pull/573/head
Rossen Stoyanchev 11 years ago
parent
commit
9aa53abdf9
  1. 57
      spring-test/src/main/java/org/springframework/mock/http/client/MockAsyncClientHttpRequest.java
  2. 46
      spring-test/src/main/java/org/springframework/test/web/client/MockRestServiceServer.java
  3. 3
      spring-test/src/main/java/org/springframework/test/web/client/RequestMatcherClientHttpRequest.java
  4. 138
      spring-test/src/test/java/org/springframework/test/web/client/samples/SampleAsyncTests.java
  5. 5
      spring-test/src/test/java/org/springframework/test/web/client/samples/SampleTests.java

57
spring-test/src/main/java/org/springframework/mock/http/client/MockAsyncClientHttpRequest.java

@ -0,0 +1,57 @@ @@ -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;
}
}

46
spring-test/src/main/java/org/springframework/test/web/client/MockRestServiceServer.java

@ -16,17 +16,25 @@ @@ -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; @@ -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 { @@ -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 { @@ -171,7 +189,6 @@ public class MockRestServiceServer {
sb.append(request.toString()).append("\n");
}
}
return sb.toString();
}
@ -180,12 +197,22 @@ public class MockRestServiceServer { @@ -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 { @@ -201,9 +228,8 @@ public class MockRestServiceServer {
request.setMethod(httpMethod);
MockRestServiceServer.this.actualRequests.add(request);
return request;
}
}
}
}

3
spring-test/src/main/java/org/springframework/test/web/client/RequestMatcherClientHttpRequest.java

@ -20,6 +20,7 @@ import java.util.LinkedList; @@ -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; @@ -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>();

138
spring-test/src/test/java/org/springframework/test/web/client/samples/SampleAsyncTests.java

@ -0,0 +1,138 @@ @@ -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"));
}
}
}

5
spring-test/src/test/java/org/springframework/test/web/client/samples/SampleTests.java

@ -61,8 +61,9 @@ public class SampleTests { @@ -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…
Cancel
Save