Separate ResponseActions from ClientHttpRequest
Before this commit RequestMatcherClientHttpRequest served both as API to define request expectations, i.e. ResponseActions, as well as the implementation of ClientHttpRequest representing actual requests. DefaultResponseActions replaces this class as a simple holder of expected requests and mock responses. MockRestServiceServer is then responsible to match request expectations and create a mock response. Issue: SPR-11365
This commit is contained in:
parent
3329abffc8
commit
37a3fa96d1
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2015 the original author or authors.
|
* Copyright 2002-2016 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -20,28 +20,26 @@ import java.io.IOException;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.springframework.http.client.ClientHttpRequest;
|
||||||
import org.springframework.http.client.ClientHttpResponse;
|
import org.springframework.http.client.ClientHttpResponse;
|
||||||
import org.springframework.mock.http.client.MockAsyncClientHttpRequest;
|
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A specialization of {@code MockClientHttpRequest} that matches the request
|
* Default implementation of {@code ResponseActions} that is also a composite
|
||||||
* against a set of expectations, via {@link RequestMatcher} instances. The
|
* {@code RequestMatcher}, invoking all request matchers it contains, as well as
|
||||||
* expectations are checked when the request is executed. This class also uses a
|
* a {@code ResponseCreator} delegating to the response creator it contains.
|
||||||
* {@link ResponseCreator} to create the response.
|
|
||||||
*
|
*
|
||||||
* @author Craig Walls
|
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
* @since 3.2
|
* @since 4.3
|
||||||
*/
|
*/
|
||||||
class RequestMatcherClientHttpRequest extends MockAsyncClientHttpRequest implements ResponseActions {
|
class DefaultResponseActions implements ResponseActions, RequestMatcher, ResponseCreator {
|
||||||
|
|
||||||
private final List<RequestMatcher> requestMatchers = new LinkedList<RequestMatcher>();
|
private final List<RequestMatcher> requestMatchers = new LinkedList<RequestMatcher>();
|
||||||
|
|
||||||
private ResponseCreator responseCreator;
|
private ResponseCreator responseCreator;
|
||||||
|
|
||||||
|
|
||||||
public RequestMatcherClientHttpRequest(RequestMatcher requestMatcher) {
|
public DefaultResponseActions(RequestMatcher requestMatcher) {
|
||||||
Assert.notNull(requestMatcher, "RequestMatcher is required");
|
Assert.notNull(requestMatcher, "RequestMatcher is required");
|
||||||
this.requestMatchers.add(requestMatcher);
|
this.requestMatchers.add(requestMatcher);
|
||||||
}
|
}
|
||||||
|
@ -61,21 +59,18 @@ class RequestMatcherClientHttpRequest extends MockAsyncClientHttpRequest impleme
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ClientHttpResponse executeInternal() throws IOException {
|
public void match(ClientHttpRequest request) throws IOException {
|
||||||
if (this.requestMatchers.isEmpty()) {
|
for (RequestMatcher matcher : this.requestMatchers) {
|
||||||
throw new AssertionError("No request expectations to execute");
|
matcher.match(request);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClientHttpResponse createResponse(ClientHttpRequest request) throws IOException {
|
||||||
if (this.responseCreator == null) {
|
if (this.responseCreator == null) {
|
||||||
throw new AssertionError("No ResponseCreator was set up. Add it after request expectations, " +
|
throw new IllegalStateException("createResponse called before ResponseCreator was set.");
|
||||||
"e.g. MockRestServiceServer.expect(requestTo(\"/foo\")).andRespond(withSuccess())");
|
|
||||||
}
|
}
|
||||||
|
return this.responseCreator.createResponse(request);
|
||||||
for (RequestMatcher requestMatcher : this.requestMatchers) {
|
|
||||||
requestMatcher.match(this);
|
|
||||||
}
|
|
||||||
setResponse(this.responseCreator.createResponse(this));
|
|
||||||
return super.executeInternal();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -27,6 +27,8 @@ import org.springframework.http.client.AsyncClientHttpRequest;
|
||||||
import org.springframework.http.client.AsyncClientHttpRequestFactory;
|
import org.springframework.http.client.AsyncClientHttpRequestFactory;
|
||||||
import org.springframework.http.client.ClientHttpRequest;
|
import org.springframework.http.client.ClientHttpRequest;
|
||||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||||
|
import org.springframework.http.client.ClientHttpResponse;
|
||||||
|
import org.springframework.mock.http.client.MockAsyncClientHttpRequest;
|
||||||
import org.springframework.test.web.client.match.MockRestRequestMatchers;
|
import org.springframework.test.web.client.match.MockRestRequestMatchers;
|
||||||
import org.springframework.test.web.client.response.MockRestResponseCreators;
|
import org.springframework.test.web.client.response.MockRestResponseCreators;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
@ -96,11 +98,11 @@ import org.springframework.web.client.support.RestGatewaySupport;
|
||||||
*/
|
*/
|
||||||
public class MockRestServiceServer {
|
public class MockRestServiceServer {
|
||||||
|
|
||||||
private final List<RequestMatcherClientHttpRequest> expectedRequests =
|
private final List<DefaultResponseActions> responseActions =
|
||||||
new LinkedList<RequestMatcherClientHttpRequest>();
|
new LinkedList<DefaultResponseActions>();
|
||||||
|
|
||||||
private final List<RequestMatcherClientHttpRequest> actualRequests =
|
private final List<MockAsyncClientHttpRequest> requests =
|
||||||
new LinkedList<RequestMatcherClientHttpRequest>();
|
new LinkedList<MockAsyncClientHttpRequest>();
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -161,9 +163,9 @@ public class MockRestServiceServer {
|
||||||
* @return used to set up further expectations or to define a response
|
* @return used to set up further expectations or to define a response
|
||||||
*/
|
*/
|
||||||
public ResponseActions expect(RequestMatcher requestMatcher) {
|
public ResponseActions expect(RequestMatcher requestMatcher) {
|
||||||
Assert.state(this.actualRequests.isEmpty(), "Can't add more expected requests with test already underway");
|
Assert.state(this.requests.isEmpty(), "Can't add more expected requests with test already underway");
|
||||||
RequestMatcherClientHttpRequest request = new RequestMatcherClientHttpRequest(requestMatcher);
|
DefaultResponseActions request = new DefaultResponseActions(requestMatcher);
|
||||||
this.expectedRequests.add(request);
|
this.responseActions.add(request);
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,7 +175,7 @@ public class MockRestServiceServer {
|
||||||
* @throws AssertionError when some expectations were not met
|
* @throws AssertionError when some expectations were not met
|
||||||
*/
|
*/
|
||||||
public void verify() {
|
public void verify() {
|
||||||
if (this.expectedRequests.isEmpty() || this.expectedRequests.equals(this.actualRequests)) {
|
if (this.responseActions.isEmpty() || this.responseActions.size() == this.requests.size()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw new AssertionError(getVerifyMessage());
|
throw new AssertionError(getVerifyMessage());
|
||||||
|
@ -181,15 +183,15 @@ public class MockRestServiceServer {
|
||||||
|
|
||||||
private String getVerifyMessage() {
|
private String getVerifyMessage() {
|
||||||
StringBuilder sb = new StringBuilder("Further request(s) expected\n");
|
StringBuilder sb = new StringBuilder("Further request(s) expected\n");
|
||||||
if (this.actualRequests.size() > 0) {
|
if (this.requests.size() > 0) {
|
||||||
sb.append("The following ");
|
sb.append("The following ");
|
||||||
}
|
}
|
||||||
sb.append(this.actualRequests.size()).append(" out of ");
|
sb.append(this.requests.size()).append(" out of ");
|
||||||
sb.append(this.expectedRequests.size()).append(" were executed");
|
sb.append(this.responseActions.size()).append(" were executed");
|
||||||
|
|
||||||
if (this.actualRequests.size() > 0) {
|
if (this.requests.size() > 0) {
|
||||||
sb.append(":\n");
|
sb.append(":\n");
|
||||||
for (RequestMatcherClientHttpRequest request : this.actualRequests) {
|
for (MockAsyncClientHttpRequest request : this.requests) {
|
||||||
sb.append(request.toString()).append("\n");
|
sb.append(request.toString()).append("\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -199,12 +201,12 @@ public class MockRestServiceServer {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mock ClientHttpRequestFactory that creates requests by iterating
|
* Mock ClientHttpRequestFactory that creates requests by iterating
|
||||||
* over the list of expected {@link RequestMatcherClientHttpRequest}'s.
|
* over the list of expected {@link DefaultResponseActions}'s.
|
||||||
*/
|
*/
|
||||||
private class RequestMatcherClientHttpRequestFactory
|
private class RequestMatcherClientHttpRequestFactory
|
||||||
implements ClientHttpRequestFactory, AsyncClientHttpRequestFactory {
|
implements ClientHttpRequestFactory, AsyncClientHttpRequestFactory {
|
||||||
|
|
||||||
private Iterator<RequestMatcherClientHttpRequest> requestIterator;
|
private Iterator<DefaultResponseActions> requestIterator;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
|
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
|
||||||
|
@ -216,24 +218,39 @@ public class MockRestServiceServer {
|
||||||
return createRequestInternal(uri, httpMethod);
|
return createRequestInternal(uri, httpMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
private RequestMatcherClientHttpRequest createRequestInternal(URI uri, HttpMethod httpMethod) {
|
private MockAsyncClientHttpRequest createRequestInternal(URI uri, HttpMethod httpMethod) {
|
||||||
Assert.notNull(uri, "'uri' must not be null");
|
Assert.notNull(uri, "'uri' must not be null");
|
||||||
Assert.notNull(httpMethod, "'httpMethod' must not be null");
|
Assert.notNull(httpMethod, "'httpMethod' must not be null");
|
||||||
|
|
||||||
if (this.requestIterator == null) {
|
MockAsyncClientHttpRequest request = new MockAsyncClientHttpRequest(httpMethod, uri) {
|
||||||
this.requestIterator = MockRestServiceServer.this.expectedRequests.iterator();
|
@Override
|
||||||
}
|
protected ClientHttpResponse executeInternal() throws IOException {
|
||||||
if (!this.requestIterator.hasNext()) {
|
ClientHttpResponse response = validateRequest(this);
|
||||||
throw new AssertionError("No further requests expected: HTTP " + httpMethod + " " + uri);
|
setResponse(response);
|
||||||
|
return response;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
RequestMatcherClientHttpRequest request = this.requestIterator.next();
|
MockRestServiceServer.this.requests.add(request);
|
||||||
request.setURI(uri);
|
|
||||||
request.setMethod(httpMethod);
|
|
||||||
|
|
||||||
MockRestServiceServer.this.actualRequests.add(request);
|
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ClientHttpResponse validateRequest(MockAsyncClientHttpRequest request)
|
||||||
|
throws IOException {
|
||||||
|
|
||||||
|
if (this.requestIterator == null) {
|
||||||
|
this.requestIterator = MockRestServiceServer.this.responseActions.iterator();
|
||||||
|
}
|
||||||
|
if (!this.requestIterator.hasNext()) {
|
||||||
|
throw new AssertionError("No further requests expected: HTTP " +
|
||||||
|
request.getMethod() + " " + request.getURI());
|
||||||
|
}
|
||||||
|
|
||||||
|
DefaultResponseActions responseActions = this.requestIterator.next();
|
||||||
|
responseActions.match(request);
|
||||||
|
|
||||||
|
return responseActions.createResponse(request);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,10 +52,8 @@ public class MockClientHttpRequestFactoryTests {
|
||||||
@Test
|
@Test
|
||||||
public void createRequest() throws Exception {
|
public void createRequest() throws Exception {
|
||||||
URI uri = new URI("/foo");
|
URI uri = new URI("/foo");
|
||||||
ClientHttpRequest expected = (ClientHttpRequest) this.server.expect(anything());
|
|
||||||
ClientHttpRequest actual = this.factory.createRequest(uri, HttpMethod.GET);
|
ClientHttpRequest actual = this.factory.createRequest(uri, HttpMethod.GET);
|
||||||
|
|
||||||
assertSame(expected, actual);
|
|
||||||
assertEquals(uri, actual.getURI());
|
assertEquals(uri, actual.getURI());
|
||||||
assertEquals(HttpMethod.GET, actual.getMethod());
|
assertEquals(HttpMethod.GET, actual.getMethod());
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue