From 08a08725be865a620373e36ceec0820e36f1fd83 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Tue, 23 Feb 2016 10:56:18 -0500 Subject: [PATCH] Allow plugging in custom RequestExpectationManager The MockRestServiceServer builder now has an option to plug in a custom RequestExpectationManager. Issue: SPR-11365 --- .../AbstractRequestExpectationManager.java | 26 ++++++-------- ...ns.java => DefaultRequestExpectation.java} | 9 +++-- .../web/client/MockRestServiceServer.java | 36 ++++++++++++------- .../test/web/client/RequestExpectation.java | 10 ++++-- .../SimpleRequestExpectationManager.java | 13 +++---- .../UnorderedRequestExpectationManager.java | 16 ++++----- ...SimpleRequestExpectationManagerTests.java} | 5 +-- 7 files changed, 61 insertions(+), 54 deletions(-) rename spring-test/src/main/java/org/springframework/test/web/client/{DefaultResponseActions.java => DefaultRequestExpectation.java} (84%) rename spring-test/src/test/java/org/springframework/test/web/client/{OrderedRequestExpectationManagerTests.java => SimpleRequestExpectationManagerTests.java} (94%) diff --git a/spring-test/src/main/java/org/springframework/test/web/client/AbstractRequestExpectationManager.java b/spring-test/src/main/java/org/springframework/test/web/client/AbstractRequestExpectationManager.java index d9908321336..de40942c33a 100644 --- a/spring-test/src/main/java/org/springframework/test/web/client/AbstractRequestExpectationManager.java +++ b/spring-test/src/main/java/org/springframework/test/web/client/AbstractRequestExpectationManager.java @@ -24,11 +24,11 @@ import org.springframework.http.client.ClientHttpResponse; import org.springframework.util.Assert; /** - * Base class for {@code RequestExpectationManager} implementations. - * Creates and contains expectations and stores actual requests. + * Base class for {@code RequestExpectationManager} implementations responsible + * for storing expectations and requests. * - *

Sub-classes are responsible for matching actual to expected requests and - * for verifying remaining expectations at the end. + *

Sub-classes are responsible for matching requests to expectations and + * verifying there are no remaining expectations at the end. * * @author Rossen Stoyanchev * @since 4.3 @@ -40,30 +40,26 @@ public abstract class AbstractRequestExpectationManager implements RequestExpect private final List requests = new LinkedList(); - public AbstractRequestExpectationManager() { - } - - public AbstractRequestExpectationManager(List expectations) { - this.expectations.addAll(expectations); - } - - - public List getExpectations() { + protected List getExpectations() { return this.expectations; } - public List getRequests() { + protected List getRequests() { return this.requests; } @Override public ResponseActions expectRequest(RequestMatcher requestMatcher) { Assert.state(getRequests().isEmpty(), "Cannot add more expectations after actual requests are made."); - DefaultResponseActions expectation = new DefaultResponseActions(requestMatcher); + RequestExpectation expectation = createExpectation(requestMatcher); getExpectations().add(expectation); return expectation; } + protected RequestExpectation createExpectation(RequestMatcher requestMatcher) { + return new DefaultRequestExpectation(requestMatcher); + } + @Override public ClientHttpResponse validateRequest(ClientHttpRequest request) throws IOException { ClientHttpResponse response = validateRequestInternal(request); diff --git a/spring-test/src/main/java/org/springframework/test/web/client/DefaultResponseActions.java b/spring-test/src/main/java/org/springframework/test/web/client/DefaultRequestExpectation.java similarity index 84% rename from spring-test/src/main/java/org/springframework/test/web/client/DefaultResponseActions.java rename to spring-test/src/main/java/org/springframework/test/web/client/DefaultRequestExpectation.java index 4b76aa1ac90..45efae1cf5c 100644 --- a/spring-test/src/main/java/org/springframework/test/web/client/DefaultResponseActions.java +++ b/spring-test/src/main/java/org/springframework/test/web/client/DefaultRequestExpectation.java @@ -25,21 +25,20 @@ import org.springframework.http.client.ClientHttpResponse; import org.springframework.util.Assert; /** - * Default implementation of {@code ResponseActions} that is also a composite - * {@code RequestMatcher}, invoking all request matchers it contains, as well as - * a {@code ResponseCreator} delegating to the response creator it contains. + * Default implementation of {@code RequestExpectation} that simply delegates + * to the request matchers and the response creator it contains. * * @author Rossen Stoyanchev * @since 4.3 */ -class DefaultResponseActions implements ResponseActions, RequestExpectation { +public class DefaultRequestExpectation implements RequestExpectation { private final List requestMatchers = new LinkedList(); private ResponseCreator responseCreator; - public DefaultResponseActions(RequestMatcher requestMatcher) { + public DefaultRequestExpectation(RequestMatcher requestMatcher) { Assert.notNull(requestMatcher, "RequestMatcher is required"); this.requestMatchers.add(requestMatcher); } diff --git a/spring-test/src/main/java/org/springframework/test/web/client/MockRestServiceServer.java b/spring-test/src/main/java/org/springframework/test/web/client/MockRestServiceServer.java index 71bae3d5730..82ebcd8a805 100644 --- a/spring-test/src/main/java/org/springframework/test/web/client/MockRestServiceServer.java +++ b/spring-test/src/main/java/org/springframework/test/web/client/MockRestServiceServer.java @@ -200,10 +200,19 @@ public class MockRestServiceServer { public interface MockRestServiceServerBuilder { /** - * When this option is set, requests can be executed in any order, i.e. - * not matching the order in which expected requests are declared. + * Allow expected requests to be executed in any order not necessarily + * matching the order of declaration. This is a shortcut for:
+ * {@code builder.expectationManager(new UnorderedRequestExpectationManager)} */ - MockRestServiceServerBuilder ignoreExpectOrder(); + MockRestServiceServerBuilder unordered(); + + /** + * Configure a custom {@code RequestExpectationManager}. + *

By default {@link SimpleRequestExpectationManager} is used. It is + * also possible to switch to {@link UnorderedRequestExpectationManager} + * by setting {@link #unordered()}. + */ + MockRestServiceServerBuilder expectationManager(RequestExpectationManager manager); /** * Build the {@code MockRestServiceServer} and setting up the underlying @@ -220,7 +229,7 @@ public class MockRestServiceServer { private final AsyncRestTemplate asyncRestTemplate; - private boolean ignoreExpectOrder; + private RequestExpectationManager expectationManager = new SimpleRequestExpectationManager(); public DefaultBuilder(RestTemplate restTemplate) { @@ -237,19 +246,21 @@ public class MockRestServiceServer { @Override - public MockRestServiceServerBuilder ignoreExpectOrder() { - this.ignoreExpectOrder = true; + public MockRestServiceServerBuilder unordered() { + expectationManager(new UnorderedRequestExpectationManager()); return this; } + @Override + public MockRestServiceServerBuilder expectationManager(RequestExpectationManager manager) { + Assert.notNull(manager, "'manager' is required."); + this.expectationManager = manager; + return this; + } @Override public MockRestServiceServer build() { - - MockRestServiceServer server = (this.ignoreExpectOrder ? - new MockRestServiceServer(new UnorderedRequestExpectationManager()) : - new MockRestServiceServer()); - + MockRestServiceServer server = new MockRestServiceServer(this.expectationManager); MockClientHttpRequestFactory factory = server.new MockClientHttpRequestFactory(); if (this.restTemplate != null) { this.restTemplate.setRequestFactory(factory); @@ -257,7 +268,6 @@ public class MockRestServiceServer { if (this.asyncRestTemplate != null) { this.asyncRestTemplate.setAsyncRequestFactory(factory); } - return server; } } @@ -265,7 +275,7 @@ public class MockRestServiceServer { /** * Mock ClientHttpRequestFactory that creates requests by iterating - * over the list of expected {@link DefaultResponseActions}'s. + * over the list of expected {@link DefaultRequestExpectation}'s. */ private class MockClientHttpRequestFactory implements ClientHttpRequestFactory, AsyncClientHttpRequestFactory { diff --git a/spring-test/src/main/java/org/springframework/test/web/client/RequestExpectation.java b/spring-test/src/main/java/org/springframework/test/web/client/RequestExpectation.java index cec8c3744b9..971139ac1a6 100644 --- a/spring-test/src/main/java/org/springframework/test/web/client/RequestExpectation.java +++ b/spring-test/src/main/java/org/springframework/test/web/client/RequestExpectation.java @@ -16,12 +16,16 @@ package org.springframework.test.web.client; /** - * A contract that combines {@code RequestMatcher} with {@code ResponseCreator} - * to define an expected request and a response to use for it. + * An extension of {@code ResponseActions} that also implements + * {@code RequestMatcher} and {@code ResponseCreator} + * + *

{@code ResponseActions} is the API for defining expectations while + * {@code RequestExpectation} is the internal SPI to match those expectations + * to actual requests and to create responses. * * @author Rossen Stoyanchev * @since 4.3 */ -public interface RequestExpectation extends RequestMatcher, ResponseCreator { +public interface RequestExpectation extends ResponseActions, RequestMatcher, ResponseCreator { } diff --git a/spring-test/src/main/java/org/springframework/test/web/client/SimpleRequestExpectationManager.java b/spring-test/src/main/java/org/springframework/test/web/client/SimpleRequestExpectationManager.java index 7a58679778d..ba37fbb18a3 100644 --- a/spring-test/src/main/java/org/springframework/test/web/client/SimpleRequestExpectationManager.java +++ b/spring-test/src/main/java/org/springframework/test/web/client/SimpleRequestExpectationManager.java @@ -24,8 +24,8 @@ import org.springframework.http.client.ClientHttpRequest; import org.springframework.http.client.ClientHttpResponse; /** - * {@code RequestExpectationManager} that expects requests to follow the order - * in which expected requests were declared. + * Simple {@code RequestExpectationManager} that matches requests to expectations + * sequentially, i.e. in the order of declaration of expectations. * * @author Rossen Stoyanchev * @since 4.3 @@ -43,7 +43,8 @@ public class SimpleRequestExpectationManager extends AbstractRequestExpectationM if (!this.iterator.hasNext()) { HttpMethod method = request.getMethod(); URI uri = request.getURI(); - throw new AssertionError("No further requests expected: HTTP " + method + " " + uri); + String firstLine = "No further requests expected: HTTP " + method + " " + uri + "\n"; + throw new AssertionError(createErrorMessage(firstLine)); } RequestExpectation expectation = this.iterator.next(); expectation.match(request); @@ -55,11 +56,11 @@ public class SimpleRequestExpectationManager extends AbstractRequestExpectationM if (getExpectations().isEmpty() || getExpectations().size() == getRequests().size()) { return; } - throw new AssertionError(getVerifyMessage()); + throw new AssertionError(createErrorMessage("Further request(s) expected\n")); } - private String getVerifyMessage() { - StringBuilder sb = new StringBuilder("Further request(s) expected\n"); + private String createErrorMessage(String firstLine) { + StringBuilder sb = new StringBuilder(firstLine); if (getRequests().size() > 0) { sb.append("The following "); } diff --git a/spring-test/src/main/java/org/springframework/test/web/client/UnorderedRequestExpectationManager.java b/spring-test/src/main/java/org/springframework/test/web/client/UnorderedRequestExpectationManager.java index 2e658d60776..eab0d4d4550 100644 --- a/spring-test/src/main/java/org/springframework/test/web/client/UnorderedRequestExpectationManager.java +++ b/spring-test/src/main/java/org/springframework/test/web/client/UnorderedRequestExpectationManager.java @@ -25,9 +25,8 @@ import org.springframework.http.client.ClientHttpRequest; import org.springframework.http.client.ClientHttpResponse; /** - * {@code RequestExpectationManager} that tries to match actual requests to - * expected requests regardless of the order in which expected requests were - * declared. + * {@code RequestExpectationManager} that matches requests to expectations + * regardless of the order of declaration of expectations. * * @author Rossen Stoyanchev * @since 4.3 @@ -37,23 +36,20 @@ public class UnorderedRequestExpectationManager extends AbstractRequestExpectati private final List remainingExpectations = new LinkedList(); - public UnorderedRequestExpectationManager() { - } - - public UnorderedRequestExpectationManager(List expectations) { - super(expectations); + protected List getRemainingExpectations() { + return this.remainingExpectations; } @Override public ClientHttpResponse validateRequestInternal(ClientHttpRequest request) throws IOException { if (getRequests().isEmpty()) { - this.remainingExpectations.addAll(getExpectations()); + getRemainingExpectations().addAll(getExpectations()); } for (RequestExpectation expectation : getExpectations()) { try { expectation.match(request); - this.remainingExpectations.remove(expectation); + getRemainingExpectations().remove(expectation); return expectation.createResponse(request); } catch (AssertionError error) { diff --git a/spring-test/src/test/java/org/springframework/test/web/client/OrderedRequestExpectationManagerTests.java b/spring-test/src/test/java/org/springframework/test/web/client/SimpleRequestExpectationManagerTests.java similarity index 94% rename from spring-test/src/test/java/org/springframework/test/web/client/OrderedRequestExpectationManagerTests.java rename to spring-test/src/test/java/org/springframework/test/web/client/SimpleRequestExpectationManagerTests.java index 755e4204ad5..3c649e5b40c 100644 --- a/spring-test/src/test/java/org/springframework/test/web/client/OrderedRequestExpectationManagerTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/client/SimpleRequestExpectationManagerTests.java @@ -34,7 +34,7 @@ import static org.springframework.test.web.client.response.MockRestResponseCreat * Unit tests for {@link AbstractRequestExpectationManager}. * @author Rossen Stoyanchev */ -public class OrderedRequestExpectationManagerTests { +public class SimpleRequestExpectationManagerTests { private SimpleRequestExpectationManager manager = new SimpleRequestExpectationManager(); @@ -45,7 +45,8 @@ public class OrderedRequestExpectationManagerTests { this.manager.validateRequest(request(HttpMethod.GET, "/foo")); } catch (AssertionError error) { - assertEquals("No further requests expected: HTTP GET /foo", error.getMessage()); + assertEquals("No further requests expected: HTTP GET /foo\n" + + "0 out of 0 were executed", error.getMessage()); } }