Add ExpectedCount
MockRestServicesServer now supports an expect variant that accepts a range of expected count of executions. Issue: SPR-11365
This commit is contained in:
parent
08a08725be
commit
91872b0d74
|
@ -16,19 +16,25 @@
|
|||
package org.springframework.test.web.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.client.ClientHttpRequest;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Base class for {@code RequestExpectationManager} implementations responsible
|
||||
* for storing expectations and requests.
|
||||
* for storing expectations and actual requests, and checking for unsatisfied
|
||||
* expectations at the end.
|
||||
*
|
||||
* <p>Sub-classes are responsible for matching requests to expectations and
|
||||
* verifying there are no remaining expectations at the end.
|
||||
* <p>Sub-classes are responsible for validating each request by matching it to
|
||||
* to expectations following the order of declaration or not.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.3
|
||||
|
@ -48,26 +54,127 @@ public abstract class AbstractRequestExpectationManager implements RequestExpect
|
|||
return this.requests;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ResponseActions expectRequest(RequestMatcher requestMatcher) {
|
||||
public ResponseActions expectRequest(ExpectedCount count, RequestMatcher matcher) {
|
||||
Assert.state(getRequests().isEmpty(), "Cannot add more expectations after actual requests are made.");
|
||||
RequestExpectation expectation = createExpectation(requestMatcher);
|
||||
RequestExpectation expectation = new DefaultRequestExpectation(count, matcher);
|
||||
getExpectations().add(expectation);
|
||||
return expectation;
|
||||
}
|
||||
|
||||
protected RequestExpectation createExpectation(RequestMatcher requestMatcher) {
|
||||
return new DefaultRequestExpectation(requestMatcher);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientHttpResponse validateRequest(ClientHttpRequest request) throws IOException {
|
||||
if (getRequests().isEmpty()) {
|
||||
afterExpectationsDeclared();
|
||||
}
|
||||
ClientHttpResponse response = validateRequestInternal(request);
|
||||
getRequests().add(request);
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked after the phase of declaring expected requests is over. This is
|
||||
* detected from {@link #validateRequest} on the first actual request.
|
||||
*/
|
||||
protected void afterExpectationsDeclared() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sub-classes must implement the actual validation of the request
|
||||
* matching it to a declared expectation.
|
||||
*/
|
||||
protected abstract ClientHttpResponse validateRequestInternal(ClientHttpRequest request)
|
||||
throws IOException;
|
||||
|
||||
@Override
|
||||
public void verify() {
|
||||
if (getExpectations().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
int count = 0;
|
||||
for (RequestExpectation expectation : getExpectations()) {
|
||||
if (!expectation.isSatisfied()) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (count > 0) {
|
||||
String message = "Further request(s) expected leaving " + count + " unsatisfied expectation(s).\n";
|
||||
throw new AssertionError(message + getRequestDetails());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return details of executed requests.
|
||||
*/
|
||||
protected String getRequestDetails() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(getRequests().size()).append(" request(s) executed");
|
||||
if (!getRequests().isEmpty()) {
|
||||
sb.append(":\n");
|
||||
for (ClientHttpRequest request : getRequests()) {
|
||||
sb.append(request.toString()).append("\n");
|
||||
}
|
||||
}
|
||||
else {
|
||||
sb.append(".\n");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an {@code AssertionError} that a sub-class can raise for an
|
||||
* unexpected request.
|
||||
*/
|
||||
protected AssertionError createUnexpectedRequestError(ClientHttpRequest request) {
|
||||
HttpMethod method = request.getMethod();
|
||||
URI uri = request.getURI();
|
||||
String message = "No further requests expected: HTTP " + method + " " + uri + "\n";
|
||||
return new AssertionError(message + getRequestDetails());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper class to manage a group of request expectations. It helps with
|
||||
* operations against the entire group such as finding a match and updating
|
||||
* (add or remove) based on expected request count.
|
||||
*/
|
||||
protected static class RequestExpectationGroup {
|
||||
|
||||
private final Set<RequestExpectation> expectations = new LinkedHashSet<RequestExpectation>();
|
||||
|
||||
|
||||
public Set<RequestExpectation> getExpectations() {
|
||||
return this.expectations;
|
||||
}
|
||||
|
||||
public void update(RequestExpectation expectation) {
|
||||
if (expectation.hasRemainingCount()) {
|
||||
getExpectations().add(expectation);
|
||||
}
|
||||
else {
|
||||
getExpectations().remove(expectation);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateAll(Collection<RequestExpectation> expectations) {
|
||||
for (RequestExpectation expectation : expectations) {
|
||||
update(expectation);
|
||||
}
|
||||
}
|
||||
|
||||
public RequestExpectation findExpectation(ClientHttpRequest request) throws IOException {
|
||||
for (RequestExpectation expectation : getExpectations()) {
|
||||
try {
|
||||
expectation.match(request);
|
||||
return expectation;
|
||||
}
|
||||
catch (AssertionError error) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -33,17 +33,38 @@ import org.springframework.util.Assert;
|
|||
*/
|
||||
public class DefaultRequestExpectation implements RequestExpectation {
|
||||
|
||||
private final RequestCount requestCount;
|
||||
|
||||
private final List<RequestMatcher> requestMatchers = new LinkedList<RequestMatcher>();
|
||||
|
||||
private ResponseCreator responseCreator;
|
||||
|
||||
|
||||
public DefaultRequestExpectation(RequestMatcher requestMatcher) {
|
||||
Assert.notNull(requestMatcher, "RequestMatcher is required");
|
||||
/**
|
||||
* Create a new request expectation that should be called a number of times
|
||||
* as indicated by {@code RequestCount}.
|
||||
* @param expectedCount the expected request expectedCount
|
||||
*/
|
||||
public DefaultRequestExpectation(ExpectedCount expectedCount, RequestMatcher requestMatcher) {
|
||||
Assert.notNull(expectedCount, "'expectedCount' is required");
|
||||
Assert.notNull(requestMatcher, "'requestMatcher' is required");
|
||||
this.requestCount = new RequestCount(expectedCount);
|
||||
this.requestMatchers.add(requestMatcher);
|
||||
}
|
||||
|
||||
|
||||
protected RequestCount getRequestCount() {
|
||||
return this.requestCount;
|
||||
}
|
||||
|
||||
protected List<RequestMatcher> getRequestMatchers() {
|
||||
return this.requestMatchers;
|
||||
}
|
||||
|
||||
protected ResponseCreator getResponseCreator() {
|
||||
return this.responseCreator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseActions andExpect(RequestMatcher requestMatcher) {
|
||||
Assert.notNull(requestMatcher, "RequestMatcher is required");
|
||||
|
@ -59,17 +80,68 @@ public class DefaultRequestExpectation implements RequestExpectation {
|
|||
|
||||
@Override
|
||||
public void match(ClientHttpRequest request) throws IOException {
|
||||
for (RequestMatcher matcher : this.requestMatchers) {
|
||||
for (RequestMatcher matcher : getRequestMatchers()) {
|
||||
matcher.match(request);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientHttpResponse createResponse(ClientHttpRequest request) throws IOException {
|
||||
if (this.responseCreator == null) {
|
||||
if (getResponseCreator() == null) {
|
||||
throw new IllegalStateException("createResponse called before ResponseCreator was set.");
|
||||
}
|
||||
return this.responseCreator.createResponse(request);
|
||||
getRequestCount().incrementAndValidate();
|
||||
return getResponseCreator().createResponse(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasRemainingCount() {
|
||||
return getRequestCount().hasRemainingCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSatisfied() {
|
||||
return getRequestCount().isSatisfied();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper class that keeps track of actual vs expected request count.
|
||||
*/
|
||||
protected static class RequestCount {
|
||||
|
||||
private final ExpectedCount expectedCount;
|
||||
|
||||
private int matchedRequestCount;
|
||||
|
||||
|
||||
public RequestCount(ExpectedCount expectedCount) {
|
||||
this.expectedCount = expectedCount;
|
||||
}
|
||||
|
||||
|
||||
public ExpectedCount getExpectedCount() {
|
||||
return this.expectedCount;
|
||||
}
|
||||
|
||||
public int getMatchedRequestCount() {
|
||||
return this.matchedRequestCount;
|
||||
}
|
||||
|
||||
public void incrementAndValidate() {
|
||||
this.matchedRequestCount++;
|
||||
if (getMatchedRequestCount() > getExpectedCount().getMaxCount()) {
|
||||
throw new AssertionError("No more calls expected.");
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasRemainingCount() {
|
||||
return (getMatchedRequestCount() < getExpectedCount().getMaxCount());
|
||||
}
|
||||
|
||||
public boolean isSatisfied() {
|
||||
return (getMatchedRequestCount() >= getExpectedCount().getMinCount());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* Copyright 2002-2016 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;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A simple type representing a range for an expected count.
|
||||
*
|
||||
* <p>Examples:
|
||||
* <pre>
|
||||
* import static org.springframework.test.web.client.ExpectedCount.*
|
||||
*
|
||||
* once()
|
||||
* manyTimes()
|
||||
* times(5)
|
||||
* min(2)
|
||||
* max(4)
|
||||
* between(2, 4)
|
||||
* </pre>
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.3
|
||||
*/
|
||||
public class ExpectedCount {
|
||||
|
||||
private final int minCount;
|
||||
|
||||
private final int maxCount;
|
||||
|
||||
|
||||
/**
|
||||
* Private constructor.
|
||||
* See static factory methods in this class.
|
||||
*/
|
||||
private ExpectedCount(int minCount, int maxCount) {
|
||||
Assert.isTrue(minCount >= 1, "minCount >= 0 is required");
|
||||
Assert.isTrue(maxCount >= minCount, "maxCount >= minCount is required");
|
||||
this.minCount = minCount;
|
||||
this.maxCount = maxCount;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the {@code min} boundary of the expected count range.
|
||||
*/
|
||||
public int getMinCount() {
|
||||
return this.minCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@code max} boundary of the expected count range.
|
||||
*/
|
||||
public int getMaxCount() {
|
||||
return this.maxCount;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Exactly once.
|
||||
*/
|
||||
public static ExpectedCount once() {
|
||||
return new ExpectedCount(1, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Many times (range of 1..Integer.MAX_VALUE).
|
||||
*/
|
||||
public static ExpectedCount manyTimes() {
|
||||
return new ExpectedCount(1, Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exactly N times.
|
||||
*/
|
||||
public static ExpectedCount times(int count) {
|
||||
Assert.isTrue(count >= 1, "'count' must be >= 1");
|
||||
return new ExpectedCount(count, count);
|
||||
}
|
||||
|
||||
/**
|
||||
* At least {@code min} number of times.
|
||||
*/
|
||||
public static ExpectedCount min(int min) {
|
||||
Assert.isTrue(min >= 1, "'min' must be >= 1");
|
||||
return new ExpectedCount(min, Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* At most {@code max} number of times.
|
||||
*/
|
||||
public static ExpectedCount max(int max) {
|
||||
Assert.isTrue(max >= 1, "'max' must be >= 1");
|
||||
return new ExpectedCount(1, max);
|
||||
}
|
||||
|
||||
/**
|
||||
* Between {@code min} and {@code max} number of times.
|
||||
*/
|
||||
public static ExpectedCount between(int min, int max) {
|
||||
return new ExpectedCount(min, max);
|
||||
}
|
||||
|
||||
}
|
|
@ -35,59 +35,33 @@ import org.springframework.web.client.support.RestGatewaySupport;
|
|||
|
||||
/**
|
||||
* <strong>Main entry point for client-side REST testing</strong>. Used for tests
|
||||
* that involve direct or indirect (through client code) use of the
|
||||
* {@link RestTemplate}. Provides a way to set up fine-grained expectations
|
||||
* on the requests that will be performed through the {@code RestTemplate} and
|
||||
* a way to define the responses to send back removing the need for an
|
||||
* actual running server.
|
||||
* that involve direct or indirect use of the {@link RestTemplate}. Provides a
|
||||
* way to set up expected requests that will be performed through the
|
||||
* {@code RestTemplate} as well as mock responses to send back thus removing the
|
||||
* need for an actual server.
|
||||
*
|
||||
* <p>Below is an example that assumes static imports from
|
||||
* {@code MockRestRequestMatchers}, {@code MockRestResponseCreators},
|
||||
* and {@code ExpectedCount}:
|
||||
*
|
||||
* <p>Below is an example:
|
||||
* <pre class="code">
|
||||
* 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;
|
||||
*
|
||||
* ...
|
||||
*
|
||||
* RestTemplate restTemplate = new RestTemplate()
|
||||
* MockRestServiceServer mockServer = MockRestServiceServer.createServer(restTemplate);
|
||||
* MockRestServiceServer server = MockRestServiceServer.restTemplate(restTemplate).build();
|
||||
*
|
||||
* mockServer.expect(requestTo("/hotels/42")).andExpect(method(HttpMethod.GET))
|
||||
* server.expect(manyTimes(), requestTo("/hotels/42")).andExpect(method(HttpMethod.GET))
|
||||
* .andRespond(withSuccess("{ \"id\" : \"42\", \"name\" : \"Holiday Inn\"}", MediaType.APPLICATION_JSON));
|
||||
*
|
||||
* Hotel hotel = restTemplate.getForObject("/hotels/{id}", Hotel.class, 42);
|
||||
* // Use the hotel instance...
|
||||
*
|
||||
* mockServer.verify();
|
||||
* // Verify all expectations met
|
||||
* server.verify();
|
||||
* </pre>
|
||||
*
|
||||
* <p>To create an instance of this class, use {@link #createServer(RestTemplate)}
|
||||
* and provide the {@code RestTemplate} to set up for the mock testing.
|
||||
*
|
||||
* <p>After that use {@link #expect(RequestMatcher)} and fluent API methods
|
||||
* {@link ResponseActions#andExpect(RequestMatcher) andExpect(RequestMatcher)} and
|
||||
* {@link ResponseActions#andRespond(ResponseCreator) andRespond(ResponseCreator)}
|
||||
* to set up request expectations and responses, most likely relying on the default
|
||||
* {@code RequestMatcher} implementations provided in {@link MockRestRequestMatchers}
|
||||
* and the {@code ResponseCreator} implementations provided in
|
||||
* {@link MockRestResponseCreators} both of which can be statically imported.
|
||||
*
|
||||
* <p>At the end of the test use {@link #verify()} to ensure all expected
|
||||
* requests were actually performed.
|
||||
*
|
||||
* <p>Note that because of the fluent API offered by this class (and related
|
||||
* classes), you can typically use the Code Completion features (i.e.
|
||||
* ctrl-space) in your IDE to set up the mocks.
|
||||
*
|
||||
* <p>An alternative to the above is to use
|
||||
* {@link MockMvcClientHttpRequestFactory} which allows executing requests
|
||||
* against a {@link org.springframework.test.web.servlet.MockMvc MockMvc}
|
||||
* instance. That allows you to process requests using your server-side code
|
||||
* but without running a server.
|
||||
*
|
||||
* <p><strong>Credits:</strong> The client-side REST testing support was
|
||||
* inspired by and initially based on similar code in the Spring WS project for
|
||||
* client-side tests involving the {@code WebServiceTemplate}.
|
||||
* <p>Note that as an alternative to the above you can also set the
|
||||
* {@link MockMvcClientHttpRequestFactory} on a {@code RestTemplate} which
|
||||
* allows executing requests against an instance of
|
||||
* {@link org.springframework.test.web.servlet.MockMvc MockMvc}.
|
||||
*
|
||||
* @author Craig Walls
|
||||
* @author Rossen Stoyanchev
|
||||
|
@ -98,14 +72,6 @@ public class MockRestServiceServer {
|
|||
private final RequestExpectationManager expectationManager;
|
||||
|
||||
|
||||
/**
|
||||
* Private constructor.
|
||||
* See static builder methods and {@code createServer} shortcut methods.
|
||||
*/
|
||||
private MockRestServiceServer() {
|
||||
this.expectationManager = new SimpleRequestExpectationManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* Private constructor with {@code RequestExpectationManager}.
|
||||
* See static builder methods and {@code createServer} shortcut methods.
|
||||
|
@ -116,17 +82,37 @@ public class MockRestServiceServer {
|
|||
|
||||
|
||||
/**
|
||||
* Set up a new HTTP request expectation. The returned {@link ResponseActions}
|
||||
* is used to set up further expectations and to define the response.
|
||||
* <p>This method may be invoked multiple times before starting the test, i.e. before
|
||||
* using the {@code RestTemplate}, to set up expectations for multiple requests.
|
||||
* @param matcher a request expectation, see {@link MockRestRequestMatchers}
|
||||
* @return used to set up further expectations or to define a response
|
||||
* Set up an expectation for a single HTTP request. The returned
|
||||
* {@link ResponseActions} can be used to set up further expectations as
|
||||
* well as to define the response.
|
||||
*
|
||||
* <p>This method may be invoked any number times before starting to make
|
||||
* request through the underlying {@code RestTemplate} in order to set up
|
||||
* all expected requests.
|
||||
*
|
||||
* @param matcher request matcher
|
||||
* @return a representation of the expectation
|
||||
*/
|
||||
public ResponseActions expect(RequestMatcher matcher) {
|
||||
return this.expectationManager.expectRequest(matcher);
|
||||
return expect(ExpectedCount.once(), matcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* An alternative to {@link #expect(RequestMatcher)} with an indication how
|
||||
* many times the request is expected to be executed.
|
||||
*
|
||||
* <p>When request expectations have an expected count greater than one, only
|
||||
* the first execution is expected to match the order of declaration. Subsequent
|
||||
* request executions may be inserted anywhere thereafter.
|
||||
*
|
||||
* @param count the expected count
|
||||
* @param matcher request matcher
|
||||
* @return a representation of the expectation
|
||||
* @since 4.3
|
||||
*/
|
||||
public ResponseActions expect(ExpectedCount count, RequestMatcher matcher) {
|
||||
return this.expectationManager.expectRequest(count, matcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that all expected requests set up via
|
||||
|
@ -139,7 +125,7 @@ public class MockRestServiceServer {
|
|||
|
||||
|
||||
/**
|
||||
* Build a {@code MockRestServiceServer} with a {@code RestTemplate}.
|
||||
* Build a {@code MockRestServiceServer} for a {@code RestTemplate}.
|
||||
* @since 4.3
|
||||
*/
|
||||
public static MockRestServiceServerBuilder restTemplate(RestTemplate restTemplate) {
|
||||
|
@ -147,7 +133,7 @@ public class MockRestServiceServer {
|
|||
}
|
||||
|
||||
/**
|
||||
* Build a {@code MockRestServiceServer} with an {@code AsyncRestTemplate}.
|
||||
* Build a {@code MockRestServiceServer} for an {@code AsyncRestTemplate}.
|
||||
* @since 4.3
|
||||
*/
|
||||
public static MockRestServiceServerBuilder asyncRestTemplate(AsyncRestTemplate asyncRestTemplate) {
|
||||
|
@ -155,7 +141,7 @@ public class MockRestServiceServer {
|
|||
}
|
||||
|
||||
/**
|
||||
* Build a {@code MockRestServiceServer} with a {@code RestGateway}.
|
||||
* Build a {@code MockRestServiceServer} for a {@code RestGateway}.
|
||||
* @since 4.3
|
||||
*/
|
||||
public static MockRestServiceServerBuilder restGateway(RestGatewaySupport restGateway) {
|
||||
|
@ -195,7 +181,6 @@ public class MockRestServiceServer {
|
|||
|
||||
/**
|
||||
* Builder to create a {@code MockRestServiceServer}.
|
||||
|
||||
*/
|
||||
public interface MockRestServiceServerBuilder {
|
||||
|
||||
|
|
|
@ -19,13 +19,23 @@ package org.springframework.test.web.client;
|
|||
* An extension of {@code ResponseActions} that also implements
|
||||
* {@code RequestMatcher} and {@code ResponseCreator}
|
||||
*
|
||||
* <p>{@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.
|
||||
* <p>While {@code ResponseActions} is the API for defining expectations this
|
||||
* sub-interface is the internal SPI for matching these expectations to actual
|
||||
* requests and for creating responses.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.3
|
||||
*/
|
||||
public interface RequestExpectation extends ResponseActions, RequestMatcher, ResponseCreator {
|
||||
|
||||
/**
|
||||
* Whether there is a remaining count of invocations for this expectation.
|
||||
*/
|
||||
boolean hasRemainingCount();
|
||||
|
||||
/**
|
||||
* Whether the requirements for this request expectation have been met.
|
||||
*/
|
||||
boolean isSatisfied();
|
||||
|
||||
}
|
||||
|
|
|
@ -21,9 +21,9 @@ import org.springframework.http.client.ClientHttpRequest;
|
|||
import org.springframework.http.client.ClientHttpResponse;
|
||||
|
||||
/**
|
||||
* Contract to manage creating HTTP request expectations, apply them to actual
|
||||
* requests (in strict or random order), and at the end verify whether all
|
||||
* expectations were met.
|
||||
* Abstraction for creating HTTP request expectations, applying them to actual
|
||||
* requests (in strict or random order), and verifying whether expectations
|
||||
* have been met.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.3
|
||||
|
@ -36,11 +36,11 @@ public interface RequestExpectationManager {
|
|||
* @param requestMatcher a request expectation
|
||||
* @return for setting up further expectations and define a response
|
||||
*/
|
||||
ResponseActions expectRequest(RequestMatcher requestMatcher);
|
||||
ResponseActions expectRequest(ExpectedCount count, RequestMatcher requestMatcher);
|
||||
|
||||
/**
|
||||
* Validate the given actual request against the declared expectations
|
||||
* raising {@link AssertionError} if not met.
|
||||
* Validate the given actual request against the declared expectations.
|
||||
* Is successful return the mock response to use or raise an error.
|
||||
* @param request the request
|
||||
* @return the response to return if the request was validated.
|
||||
* @throws AssertionError when some expectations were not met
|
||||
|
|
|
@ -23,6 +23,9 @@ import org.springframework.http.client.ClientHttpRequest;
|
|||
/**
|
||||
* A contract for matching requests to expectations.
|
||||
*
|
||||
* <p>See {@link org.springframework.test.web.client.match.MockRestRequestMatchers
|
||||
* MockRestRequestMatchers} for static factory methods.
|
||||
*
|
||||
* @author Craig Walls
|
||||
* @since 3.2
|
||||
*/
|
||||
|
|
|
@ -16,64 +16,59 @@
|
|||
package org.springframework.test.web.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.client.ClientHttpRequest;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Simple {@code RequestExpectationManager} that matches requests to expectations
|
||||
* sequentially, i.e. in the order of declaration of expectations.
|
||||
*
|
||||
* <p>When request expectations have an expected count greater than one, only
|
||||
* the first execution is expected to match the order of declaration. Subsequent
|
||||
* request executions may be inserted anywhere thereafter.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.3
|
||||
*/
|
||||
public class SimpleRequestExpectationManager extends AbstractRequestExpectationManager {
|
||||
|
||||
private Iterator<RequestExpectation> iterator;
|
||||
private Iterator<RequestExpectation> expectationIterator;
|
||||
|
||||
private final RequestExpectationGroup repeatExpectations = new RequestExpectationGroup();
|
||||
|
||||
|
||||
@Override
|
||||
protected void afterExpectationsDeclared() {
|
||||
Assert.state(this.expectationIterator == null);
|
||||
this.expectationIterator = getExpectations().iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientHttpResponse validateRequestInternal(ClientHttpRequest request) throws IOException {
|
||||
if (this.iterator == null) {
|
||||
this.iterator = getExpectations().iterator();
|
||||
RequestExpectation expectation;
|
||||
try {
|
||||
expectation = next(request);
|
||||
expectation.match(request);
|
||||
}
|
||||
if (!this.iterator.hasNext()) {
|
||||
HttpMethod method = request.getMethod();
|
||||
URI uri = request.getURI();
|
||||
String firstLine = "No further requests expected: HTTP " + method + " " + uri + "\n";
|
||||
throw new AssertionError(createErrorMessage(firstLine));
|
||||
}
|
||||
RequestExpectation expectation = this.iterator.next();
|
||||
expectation.match(request);
|
||||
return expectation.createResponse(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void verify() {
|
||||
if (getExpectations().isEmpty() || getExpectations().size() == getRequests().size()) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError(createErrorMessage("Further request(s) expected\n"));
|
||||
}
|
||||
|
||||
private String createErrorMessage(String firstLine) {
|
||||
StringBuilder sb = new StringBuilder(firstLine);
|
||||
if (getRequests().size() > 0) {
|
||||
sb.append("The following ");
|
||||
}
|
||||
sb.append(getRequests().size()).append(" out of ");
|
||||
sb.append(getExpectations().size()).append(" were executed");
|
||||
|
||||
if (getRequests().size() > 0) {
|
||||
sb.append(":\n");
|
||||
for (ClientHttpRequest request : getRequests()) {
|
||||
sb.append(request.toString()).append("\n");
|
||||
catch (AssertionError error) {
|
||||
expectation = this.repeatExpectations.findExpectation(request);
|
||||
if (expectation == null) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
ClientHttpResponse response = expectation.createResponse(request);
|
||||
this.repeatExpectations.update(expectation);
|
||||
return response;
|
||||
}
|
||||
|
||||
private RequestExpectation next(ClientHttpRequest request) {
|
||||
if (this.expectationIterator.hasNext()) {
|
||||
return this.expectationIterator.next();
|
||||
}
|
||||
throw createUnexpectedRequestError(request);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,74 +16,36 @@
|
|||
package org.springframework.test.web.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.client.ClientHttpRequest;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
|
||||
/**
|
||||
* {@code RequestExpectationManager} that matches requests to expectations
|
||||
* regardless of the order of declaration of expectations.
|
||||
* regardless of the order of declaration of expectated requests.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.3
|
||||
*/
|
||||
public class UnorderedRequestExpectationManager extends AbstractRequestExpectationManager {
|
||||
|
||||
private final List<RequestExpectation> remainingExpectations = new LinkedList<RequestExpectation>();
|
||||
private final RequestExpectationGroup remainingExpectations = new RequestExpectationGroup();
|
||||
|
||||
|
||||
protected List<RequestExpectation> getRemainingExpectations() {
|
||||
return this.remainingExpectations;
|
||||
@Override
|
||||
protected void afterExpectationsDeclared() {
|
||||
this.remainingExpectations.updateAll(getExpectations());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ClientHttpResponse validateRequestInternal(ClientHttpRequest request) throws IOException {
|
||||
if (getRequests().isEmpty()) {
|
||||
getRemainingExpectations().addAll(getExpectations());
|
||||
RequestExpectation expectation = this.remainingExpectations.findExpectation(request);
|
||||
if (expectation != null) {
|
||||
ClientHttpResponse response = expectation.createResponse(request);
|
||||
this.remainingExpectations.update(expectation);
|
||||
return response;
|
||||
}
|
||||
for (RequestExpectation expectation : getExpectations()) {
|
||||
try {
|
||||
expectation.match(request);
|
||||
getRemainingExpectations().remove(expectation);
|
||||
return expectation.createResponse(request);
|
||||
}
|
||||
catch (AssertionError error) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
HttpMethod method = request.getMethod();
|
||||
URI uri = request.getURI();
|
||||
throw new AssertionError("Unexpected request: HTTP " + method + " " + uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void verify() {
|
||||
if (getExpectations().isEmpty() || this.remainingExpectations.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError(getVerifyMessage());
|
||||
}
|
||||
|
||||
private String getVerifyMessage() {
|
||||
StringBuilder sb = new StringBuilder("Further request(s) expected\n");
|
||||
if (getRequests().size() > 0) {
|
||||
sb.append("The following ");
|
||||
}
|
||||
sb.append(getRequests().size()).append(" were executed");
|
||||
sb.append(" leaving ").append(this.remainingExpectations.size()).append(" expectations.");
|
||||
|
||||
if (getRequests().size() > 0) {
|
||||
sb.append(":\n");
|
||||
for (ClientHttpRequest request : getRequests()) {
|
||||
sb.append(request.toString()).append("\n");
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
throw createUnexpectedRequestError(request);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* Copyright 2002-2016 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;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.client.ClientHttpRequest;
|
||||
import org.springframework.mock.http.client.MockAsyncClientHttpRequest;
|
||||
|
||||
import static junit.framework.TestCase.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.springframework.http.HttpMethod.GET;
|
||||
import static org.springframework.http.HttpMethod.POST;
|
||||
import static org.springframework.test.web.client.ExpectedCount.once;
|
||||
import static org.springframework.test.web.client.ExpectedCount.times;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link DefaultRequestExpectation}.
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class DefaultRequestExpectationTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
|
||||
@Test
|
||||
public void match() throws Exception {
|
||||
RequestExpectation expectation = new DefaultRequestExpectation(once(), requestTo("/foo"));
|
||||
expectation.match(createRequest(GET, "/foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchWithFailedExpection() throws Exception {
|
||||
RequestExpectation expectation = new DefaultRequestExpectation(once(), requestTo("/foo"));
|
||||
expectation.andExpect(method(POST));
|
||||
|
||||
this.thrown.expectMessage("Unexpected HttpMethod expected:<POST> but was:<GET>");
|
||||
expectation.match(createRequest(GET, "/foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasRemainingCount() throws Exception {
|
||||
RequestExpectation expectation = new DefaultRequestExpectation(times(2), requestTo("/foo"));
|
||||
expectation.andRespond(withSuccess());
|
||||
|
||||
expectation.createResponse(createRequest(GET, "/foo"));
|
||||
assertTrue(expectation.hasRemainingCount());
|
||||
|
||||
expectation.createResponse(createRequest(GET, "/foo"));
|
||||
assertFalse(expectation.hasRemainingCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSatisfied() throws Exception {
|
||||
RequestExpectation expectation = new DefaultRequestExpectation(times(2), requestTo("/foo"));
|
||||
expectation.andRespond(withSuccess());
|
||||
|
||||
expectation.createResponse(createRequest(GET, "/foo"));
|
||||
assertFalse(expectation.isSatisfied());
|
||||
|
||||
expectation.createResponse(createRequest(GET, "/foo"));
|
||||
assertTrue(expectation.isSatisfied());
|
||||
}
|
||||
|
||||
|
||||
|
||||
private ClientHttpRequest createRequest(HttpMethod method, String url) {
|
||||
try {
|
||||
return new MockAsyncClientHttpRequest(method, new URI(url));
|
||||
}
|
||||
catch (URISyntaxException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -19,67 +19,149 @@ package org.springframework.test.web.client;
|
|||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.client.ClientHttpRequest;
|
||||
import org.springframework.mock.http.client.MockAsyncClientHttpRequest;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.springframework.test.web.client.match.MockRestRequestMatchers.anything;
|
||||
import static org.springframework.http.HttpMethod.GET;
|
||||
import static org.springframework.http.HttpMethod.POST;
|
||||
import static org.springframework.test.web.client.ExpectedCount.max;
|
||||
import static org.springframework.test.web.client.ExpectedCount.min;
|
||||
import static org.springframework.test.web.client.ExpectedCount.once;
|
||||
import static org.springframework.test.web.client.ExpectedCount.times;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link AbstractRequestExpectationManager}.
|
||||
* Unit tests for {@link SimpleRequestExpectationManager}.
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class SimpleRequestExpectationManagerTests {
|
||||
|
||||
private SimpleRequestExpectationManager manager = new SimpleRequestExpectationManager();
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
|
||||
@Test
|
||||
public void validateWithUnexpectedRequest() throws Exception {
|
||||
public void unexpectedRequest() throws Exception {
|
||||
try {
|
||||
this.manager.validateRequest(request(HttpMethod.GET, "/foo"));
|
||||
this.manager.validateRequest(createRequest(GET, "/foo"));
|
||||
}
|
||||
catch (AssertionError error) {
|
||||
assertEquals("No further requests expected: HTTP GET /foo\n" +
|
||||
"0 out of 0 were executed", error.getMessage());
|
||||
"0 request(s) executed.\n", error.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verify() throws Exception {
|
||||
this.manager.expectRequest(anything()).andRespond(withSuccess());
|
||||
this.manager.expectRequest(anything()).andRespond(withSuccess());
|
||||
|
||||
this.manager.validateRequest(request(HttpMethod.GET, "/foo"));
|
||||
this.manager.validateRequest(request(HttpMethod.POST, "/bar"));
|
||||
public void zeroExpectedRequests() throws Exception {
|
||||
this.manager.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyWithZeroExpectations() throws Exception {
|
||||
public void sequentialRequests() throws Exception {
|
||||
this.manager.expectRequest(once(), requestTo("/foo")).andExpect(method(GET)).andRespond(withSuccess());
|
||||
this.manager.expectRequest(once(), requestTo("/bar")).andExpect(method(GET)).andRespond(withSuccess());
|
||||
|
||||
this.manager.validateRequest(createRequest(GET, "/foo"));
|
||||
this.manager.validateRequest(createRequest(GET, "/bar"));
|
||||
this.manager.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyWithRemainingExpectations() throws Exception {
|
||||
this.manager.expectRequest(anything()).andRespond(withSuccess());
|
||||
this.manager.expectRequest(anything()).andRespond(withSuccess());
|
||||
public void sequentialRequestsTooMany() throws Exception {
|
||||
this.manager.expectRequest(max(1), requestTo("/foo")).andExpect(method(GET)).andRespond(withSuccess());
|
||||
this.manager.expectRequest(max(1), requestTo("/bar")).andExpect(method(GET)).andRespond(withSuccess());
|
||||
|
||||
this.manager.validateRequest(request(HttpMethod.GET, "/foo"));
|
||||
try {
|
||||
this.manager.verify();
|
||||
}
|
||||
catch (AssertionError error) {
|
||||
assertTrue(error.getMessage(), error.getMessage().contains("1 out of 2 were executed"));
|
||||
}
|
||||
this.thrown.expectMessage("No further requests expected: HTTP GET /baz\n" +
|
||||
"2 request(s) executed:\n" +
|
||||
"GET /foo\n" +
|
||||
"GET /bar\n");
|
||||
|
||||
this.manager.validateRequest(createRequest(GET, "/foo"));
|
||||
this.manager.validateRequest(createRequest(GET, "/bar"));
|
||||
this.manager.validateRequest(createRequest(GET, "/baz"));
|
||||
}
|
||||
|
||||
private ClientHttpRequest request(HttpMethod method, String url) {
|
||||
@Test
|
||||
public void sequentialRequestsTooFew() throws Exception {
|
||||
this.manager.expectRequest(min(1), requestTo("/foo")).andExpect(method(GET)).andRespond(withSuccess());
|
||||
this.manager.expectRequest(min(1), requestTo("/bar")).andExpect(method(GET)).andRespond(withSuccess());
|
||||
|
||||
this.thrown.expectMessage("Further request(s) expected leaving 1 unsatisfied expectation(s).\n" +
|
||||
"1 request(s) executed:\nGET /foo\n");
|
||||
|
||||
this.manager.validateRequest(createRequest(GET, "/foo"));
|
||||
this.manager.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void repeatedRequests() throws Exception {
|
||||
this.manager.expectRequest(times(2), requestTo("/foo")).andExpect(method(GET)).andRespond(withSuccess());
|
||||
this.manager.expectRequest(times(2), requestTo("/bar")).andExpect(method(GET)).andRespond(withSuccess());
|
||||
|
||||
this.manager.validateRequest(createRequest(GET, "/foo"));
|
||||
this.manager.validateRequest(createRequest(GET, "/bar"));
|
||||
this.manager.validateRequest(createRequest(GET, "/foo"));
|
||||
this.manager.validateRequest(createRequest(GET, "/bar"));
|
||||
this.manager.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void repeatedRequestsTooMany() throws Exception {
|
||||
this.manager.expectRequest(max(2), requestTo("/foo")).andExpect(method(GET)).andRespond(withSuccess());
|
||||
this.manager.expectRequest(max(2), requestTo("/bar")).andExpect(method(GET)).andRespond(withSuccess());
|
||||
|
||||
this.thrown.expectMessage("No further requests expected: HTTP GET /foo\n" +
|
||||
"4 request(s) executed:\n" +
|
||||
"GET /foo\n" +
|
||||
"GET /bar\n" +
|
||||
"GET /foo\n" +
|
||||
"GET /bar\n");
|
||||
|
||||
this.manager.validateRequest(createRequest(GET, "/foo"));
|
||||
this.manager.validateRequest(createRequest(GET, "/bar"));
|
||||
this.manager.validateRequest(createRequest(GET, "/foo"));
|
||||
this.manager.validateRequest(createRequest(GET, "/bar"));
|
||||
this.manager.validateRequest(createRequest(GET, "/foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void repeatedRequestsTooFew() throws Exception {
|
||||
this.manager.expectRequest(min(2), requestTo("/foo")).andExpect(method(GET)).andRespond(withSuccess());
|
||||
this.manager.expectRequest(min(2), requestTo("/bar")).andExpect(method(GET)).andRespond(withSuccess());
|
||||
|
||||
this.thrown.expectMessage("3 request(s) executed:\n" +
|
||||
"GET /foo\n" +
|
||||
"GET /bar\n" +
|
||||
"GET /foo\n");
|
||||
|
||||
this.manager.validateRequest(createRequest(GET, "/foo"));
|
||||
this.manager.validateRequest(createRequest(GET, "/bar"));
|
||||
this.manager.validateRequest(createRequest(GET, "/foo"));
|
||||
this.manager.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void repeatedRequestsNotInOrder() throws Exception {
|
||||
this.manager.expectRequest(times(2), requestTo("/foo")).andExpect(method(GET)).andRespond(withSuccess());
|
||||
this.manager.expectRequest(times(2), requestTo("/bar")).andExpect(method(GET)).andRespond(withSuccess());
|
||||
this.manager.expectRequest(times(2), requestTo("/baz")).andExpect(method(GET)).andRespond(withSuccess());
|
||||
|
||||
this.thrown.expectMessage("Unexpected HttpMethod expected:<GET> but was:<POST>");
|
||||
this.manager.validateRequest(createRequest(POST, "/foo"));
|
||||
}
|
||||
|
||||
|
||||
private ClientHttpRequest createRequest(HttpMethod method, String url) {
|
||||
try {
|
||||
return new MockAsyncClientHttpRequest(method, new URI(url));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* Copyright 2002-2016 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;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.client.ClientHttpRequest;
|
||||
import org.springframework.mock.http.client.MockAsyncClientHttpRequest;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.springframework.http.HttpMethod.GET;
|
||||
import static org.springframework.test.web.client.ExpectedCount.max;
|
||||
import static org.springframework.test.web.client.ExpectedCount.min;
|
||||
import static org.springframework.test.web.client.ExpectedCount.once;
|
||||
import static org.springframework.test.web.client.ExpectedCount.times;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link UnorderedRequestExpectationManager}.
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class UnorderedRequestExpectationManagerTests {
|
||||
|
||||
private UnorderedRequestExpectationManager manager = new UnorderedRequestExpectationManager();
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
|
||||
@Test
|
||||
public void unexpectedRequest() throws Exception {
|
||||
try {
|
||||
this.manager.validateRequest(createRequest(GET, "/foo"));
|
||||
}
|
||||
catch (AssertionError error) {
|
||||
assertEquals("No further requests expected: HTTP GET /foo\n" +
|
||||
"0 request(s) executed.\n", error.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void zeroExpectedRequests() throws Exception {
|
||||
this.manager.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipleRequests() throws Exception {
|
||||
this.manager.expectRequest(once(), requestTo("/foo")).andExpect(method(GET)).andRespond(withSuccess());
|
||||
this.manager.expectRequest(once(), requestTo("/bar")).andExpect(method(GET)).andRespond(withSuccess());
|
||||
|
||||
this.manager.validateRequest(createRequest(GET, "/bar"));
|
||||
this.manager.validateRequest(createRequest(GET, "/foo"));
|
||||
this.manager.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void repeatedRequests() throws Exception {
|
||||
this.manager.expectRequest(times(2), requestTo("/foo")).andExpect(method(GET)).andRespond(withSuccess());
|
||||
this.manager.expectRequest(times(2), requestTo("/bar")).andExpect(method(GET)).andRespond(withSuccess());
|
||||
|
||||
this.manager.validateRequest(createRequest(GET, "/bar"));
|
||||
this.manager.validateRequest(createRequest(GET, "/foo"));
|
||||
this.manager.validateRequest(createRequest(GET, "/foo"));
|
||||
this.manager.validateRequest(createRequest(GET, "/bar"));
|
||||
this.manager.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void repeatedRequestsTooMany() throws Exception {
|
||||
this.manager.expectRequest(max(2), requestTo("/foo")).andExpect(method(GET)).andRespond(withSuccess());
|
||||
this.manager.expectRequest(max(2), requestTo("/bar")).andExpect(method(GET)).andRespond(withSuccess());
|
||||
|
||||
this.thrown.expectMessage("No further requests expected: HTTP GET /foo\n" +
|
||||
"4 request(s) executed:\n" +
|
||||
"GET /bar\n" +
|
||||
"GET /foo\n" +
|
||||
"GET /bar\n" +
|
||||
"GET /foo\n");
|
||||
|
||||
this.manager.validateRequest(createRequest(GET, "/bar"));
|
||||
this.manager.validateRequest(createRequest(GET, "/foo"));
|
||||
this.manager.validateRequest(createRequest(GET, "/bar"));
|
||||
this.manager.validateRequest(createRequest(GET, "/foo"));
|
||||
this.manager.validateRequest(createRequest(GET, "/foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void repeatedRequestsTooFew() throws Exception {
|
||||
this.manager.expectRequest(min(2), requestTo("/foo")).andExpect(method(GET)).andRespond(withSuccess());
|
||||
this.manager.expectRequest(min(2), requestTo("/bar")).andExpect(method(GET)).andRespond(withSuccess());
|
||||
|
||||
this.thrown.expectMessage("3 request(s) executed:\n" +
|
||||
"GET /bar\n" +
|
||||
"GET /foo\n" +
|
||||
"GET /foo\n");
|
||||
|
||||
this.manager.validateRequest(createRequest(GET, "/bar"));
|
||||
this.manager.validateRequest(createRequest(GET, "/foo"));
|
||||
this.manager.validateRequest(createRequest(GET, "/foo"));
|
||||
this.manager.verify();
|
||||
}
|
||||
|
||||
|
||||
private ClientHttpRequest createRequest(HttpMethod method, String url) {
|
||||
try {
|
||||
return new MockAsyncClientHttpRequest(method, new URI(url));
|
||||
}
|
||||
catch (URISyntaxException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* Copyright 2002-2016 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.
|
||||
|
@ -24,11 +24,13 @@ 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.ExpectedCount;
|
||||
import org.springframework.test.web.client.MockRestServiceServer;
|
||||
import org.springframework.util.concurrent.ListenableFuture;
|
||||
import org.springframework.web.client.AsyncRestTemplate;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.test.web.client.ExpectedCount.manyTimes;
|
||||
import static org.springframework.test.web.client.match.MockRestRequestMatchers.*;
|
||||
import static org.springframework.test.web.client.response.MockRestResponseCreators.*;
|
||||
|
||||
|
@ -63,7 +65,8 @@ public class SampleAsyncTests {
|
|||
.andRespond(withSuccess(responseBody, MediaType.APPLICATION_JSON));
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
ListenableFuture<ResponseEntity<Person>> ludwig = restTemplate.getForEntity("/composers/{id}", Person.class, 42);
|
||||
ListenableFuture<ResponseEntity<Person>> ludwig =
|
||||
this.restTemplate.getForEntity("/composers/{id}", Person.class, 42);
|
||||
|
||||
// We are only validating the request. The response is mocked out.
|
||||
// person.getName().equals("Ludwig van Beethoven")
|
||||
|
@ -73,19 +76,26 @@ public class SampleAsyncTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void performGetAsync() throws Exception {
|
||||
public void performGetManyTimes() throws Exception {
|
||||
|
||||
String responseBody = "{\"name\" : \"Ludwig van Beethoven\", \"someDouble\" : \"1.6035\"}";
|
||||
|
||||
this.mockServer.expect(requestTo("/composers/42")).andExpect(method(HttpMethod.GET))
|
||||
this.mockServer.expect(manyTimes(), 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);
|
||||
ListenableFuture<ResponseEntity<Person>> ludwig =
|
||||
this.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.restTemplate.getForEntity("/composers/{id}", Person.class, 42);
|
||||
this.restTemplate.getForEntity("/composers/{id}", Person.class, 42);
|
||||
this.restTemplate.getForEntity("/composers/{id}", Person.class, 42);
|
||||
this.restTemplate.getForEntity("/composers/{id}", Person.class, 42);
|
||||
|
||||
this.mockServer.verify();
|
||||
}
|
||||
|
||||
|
@ -98,7 +108,8 @@ public class SampleAsyncTests {
|
|||
.andRespond(withSuccess(responseBody, MediaType.APPLICATION_JSON));
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
ListenableFuture<ResponseEntity<Person>> ludwig = restTemplate.getForEntity("/composers/{id}", Person.class, 42);
|
||||
ListenableFuture<ResponseEntity<Person>> ludwig =
|
||||
this.restTemplate.getForEntity("/composers/{id}", Person.class, 42);
|
||||
|
||||
// hotel.getId() == 42
|
||||
// hotel.getName().equals("Holiday Inn")
|
||||
|
@ -132,7 +143,7 @@ public class SampleAsyncTests {
|
|||
this.mockServer.verify();
|
||||
}
|
||||
catch (AssertionError error) {
|
||||
assertTrue(error.getMessage(), error.getMessage().contains("2 out of 4 were executed"));
|
||||
assertTrue(error.getMessage(), error.getMessage().contains("2 unsatisfied expectation(s)"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,10 +23,12 @@ import org.springframework.core.io.Resource;
|
|||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.Person;
|
||||
import org.springframework.test.web.client.ExpectedCount;
|
||||
import org.springframework.test.web.client.MockRestServiceServer;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.springframework.test.web.client.ExpectedCount.manyTimes;
|
||||
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;
|
||||
|
@ -60,7 +62,7 @@ public class SampleTests {
|
|||
.andRespond(withSuccess(responseBody, MediaType.APPLICATION_JSON));
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
Person ludwig = restTemplate.getForObject("/composers/{id}", Person.class, 42);
|
||||
Person ludwig = this.restTemplate.getForObject("/composers/{id}", Person.class, 42);
|
||||
|
||||
// We are only validating the request. The response is mocked out.
|
||||
// hotel.getId() == 42
|
||||
|
@ -69,6 +71,28 @@ public class SampleTests {
|
|||
this.mockServer.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void performGetManyTimes() throws Exception {
|
||||
|
||||
String responseBody = "{\"name\" : \"Ludwig van Beethoven\", \"someDouble\" : \"1.6035\"}";
|
||||
|
||||
this.mockServer.expect(manyTimes(), requestTo("/composers/42")).andExpect(method(HttpMethod.GET))
|
||||
.andRespond(withSuccess(responseBody, MediaType.APPLICATION_JSON));
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
Person ludwig = this.restTemplate.getForObject("/composers/{id}", Person.class, 42);
|
||||
|
||||
// We are only validating the request. The response is mocked out.
|
||||
// hotel.getId() == 42
|
||||
// hotel.getName().equals("Holiday Inn")
|
||||
|
||||
this.restTemplate.getForObject("/composers/{id}", Person.class, 42);
|
||||
this.restTemplate.getForObject("/composers/{id}", Person.class, 42);
|
||||
this.restTemplate.getForObject("/composers/{id}", Person.class, 42);
|
||||
|
||||
this.mockServer.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void performGetWithResponseBodyFromFile() throws Exception {
|
||||
|
||||
|
@ -78,7 +102,7 @@ public class SampleTests {
|
|||
.andRespond(withSuccess(responseBody, MediaType.APPLICATION_JSON));
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
Person ludwig = restTemplate.getForObject("/composers/{id}", Person.class, 42);
|
||||
Person ludwig = this.restTemplate.getForObject("/composers/{id}", Person.class, 42);
|
||||
|
||||
// hotel.getId() == 42
|
||||
// hotel.getName().equals("Holiday Inn")
|
||||
|
@ -112,7 +136,7 @@ public class SampleTests {
|
|||
this.mockServer.verify();
|
||||
}
|
||||
catch (AssertionError error) {
|
||||
assertTrue(error.getMessage(), error.getMessage().contains("2 out of 4 were executed"));
|
||||
assertTrue(error.getMessage(), error.getMessage().contains("2 unsatisfied expectation(s)"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5045,8 +5045,9 @@ Here is an example:
|
|||
----
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
|
||||
MockRestServiceServer mockServer = MockRestServiceServer.createServer(restTemplate);
|
||||
mockServer.expect(requestTo("/greeting")).andRespond(withSuccess("Hello world", MediaType.TEXT_PLAIN));
|
||||
MockRestServiceServer mockServer = MockRestServiceServer.restTemplate(restTemplate).build();
|
||||
mockServer.expect(manyTimes(), requestTo("/greeting"))
|
||||
.andRespond(withSuccess("Hello world", MediaType.TEXT_PLAIN));
|
||||
|
||||
// Test code that uses the above RestTemplate ...
|
||||
|
||||
|
|
|
@ -678,4 +678,5 @@ Spring 4.3 also improves the caching abstraction as follows:
|
|||
* The JUnit support in the _Spring TestContext Framework_ now requires JUnit 4.12 or higher.
|
||||
* Server-side Spring MVC Test supports expectations on response headers with multiple values.
|
||||
* Server-side Spring MVC Test parses form data request content and populates request parameters.
|
||||
* Client-side Spring MVC Test supports expected count of request executions (once, manyTimes, min, max, etc.)
|
||||
* Client-side Spring MVC Test supports expectations for form data in the request body.
|
||||
|
|
Loading…
Reference in New Issue