This commit is contained in:
Rossen Stoyanchev 2017-07-25 12:27:33 +02:00
parent 29c112c010
commit d20e5fcc59
6 changed files with 75 additions and 54 deletions

View File

@ -19,6 +19,7 @@ package org.springframework.test.web.client;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -49,26 +50,32 @@ public abstract class AbstractRequestExpectationManager implements RequestExpect
private final List<ClientHttpRequest> requests = new LinkedList<>(); private final List<ClientHttpRequest> requests = new LinkedList<>();
/**
* Return a read-only list of the expectations.
*/
protected List<RequestExpectation> getExpectations() { protected List<RequestExpectation> getExpectations() {
return this.expectations; return Collections.unmodifiableList(this.expectations);
} }
/**
* Return a read-only list of requests executed so far.
*/
protected List<ClientHttpRequest> getRequests() { protected List<ClientHttpRequest> getRequests() {
return this.requests; return Collections.unmodifiableList(this.requests);
} }
@Override @Override
public ResponseActions expectRequest(ExpectedCount count, RequestMatcher matcher) { public ResponseActions expectRequest(ExpectedCount count, RequestMatcher matcher) {
Assert.state(getRequests().isEmpty(), "Cannot add more expectations after actual requests are made"); Assert.state(this.requests.isEmpty(), "Cannot add more expectations after actual requests are made");
RequestExpectation expectation = new DefaultRequestExpectation(count, matcher); RequestExpectation expectation = new DefaultRequestExpectation(count, matcher);
getExpectations().add(expectation); this.expectations.add(expectation);
return expectation; return expectation;
} }
@Override @Override
public ClientHttpResponse validateRequest(ClientHttpRequest request) throws IOException { public ClientHttpResponse validateRequest(ClientHttpRequest request) throws IOException {
List<ClientHttpRequest> requests = getRequests(); List<ClientHttpRequest> requests = this.requests;
synchronized (requests) { synchronized (requests) {
if (requests.isEmpty()) { if (requests.isEmpty()) {
afterExpectationsDeclared(); afterExpectationsDeclared();
@ -80,25 +87,26 @@ public abstract class AbstractRequestExpectationManager implements RequestExpect
} }
/** /**
* Invoked after the phase of declaring expected requests is over. This is * Invoked at the time of the first actual request, which effectively means
* detected from {@link #validateRequest} on the first actual request. * the expectations declaration phase is over.
*/ */
protected void afterExpectationsDeclared() { protected void afterExpectationsDeclared() {
} }
/** /**
* Subclasses must implement the actual validation of the request * Sub-classes must implement the actual validation of the request
* matching it to a declared expectation. * matching to declared expectations.
*/ */
protected abstract ClientHttpResponse validateRequestInternal(ClientHttpRequest request) throws IOException; protected abstract ClientHttpResponse validateRequestInternal(ClientHttpRequest request)
throws IOException;
@Override @Override
public void verify() { public void verify() {
if (getExpectations().isEmpty()) { if (this.expectations.isEmpty()) {
return; return;
} }
int count = 0; int count = 0;
for (RequestExpectation expectation : getExpectations()) { for (RequestExpectation expectation : this.expectations) {
if (!expectation.isSatisfied()) { if (!expectation.isSatisfied()) {
count++; count++;
} }
@ -114,10 +122,10 @@ public abstract class AbstractRequestExpectationManager implements RequestExpect
*/ */
protected String getRequestDetails() { protected String getRequestDetails() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append(getRequests().size()).append(" request(s) executed"); sb.append(this.requests.size()).append(" request(s) executed");
if (!getRequests().isEmpty()) { if (!this.requests.isEmpty()) {
sb.append(":\n"); sb.append(":\n");
for (ClientHttpRequest request : getRequests()) { for (ClientHttpRequest request : this.requests) {
sb.append(request.toString()).append("\n"); sb.append(request.toString()).append("\n");
} }
} }
@ -146,9 +154,7 @@ public abstract class AbstractRequestExpectationManager implements RequestExpect
/** /**
* Helper class to manage a group of request expectations. It helps with * Helper class to manage a group of remaining expectations.
* operations against the entire group such as finding a match and updating
* (add or remove) based on expected request count.
*/ */
protected static class RequestExpectationGroup { protected static class RequestExpectationGroup {
@ -158,24 +164,10 @@ public abstract class AbstractRequestExpectationManager implements RequestExpect
return this.expectations; 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);
}
}
@Nullable @Nullable
public RequestExpectation findExpectation(ClientHttpRequest request) throws IOException { public RequestExpectation findExpectation(ClientHttpRequest request) throws IOException {
for (RequestExpectation expectation : getExpectations()) { for (RequestExpectation expectation : this.expectations) {
try { try {
expectation.match(request); expectation.match(request);
return expectation; return expectation;
@ -187,8 +179,30 @@ public abstract class AbstractRequestExpectationManager implements RequestExpect
return null; return null;
} }
/**
* Invoke this for an expectation that has been matched.
* <p>The given expectation will either be stored if it has a remaining
* count or it will be removed otherwise.
*/
public void update(RequestExpectation expectation) {
if (expectation.hasRemainingCount()) {
this.expectations.add(expectation);
}
else {
this.expectations.remove(expectation);
}
}
/**
* Collection variant of {@link #update(RequestExpectation)} that can
* be used to insert expectations.
*/
public void updateAll(Collection<RequestExpectation> expectations) {
expectations.forEach(this::update);
}
public void reset() { public void reset() {
getExpectations().clear(); this.expectations.clear();
} }
} }

View File

@ -273,7 +273,8 @@ public class MockRestServiceServer {
* Mock ClientHttpRequestFactory that creates requests by iterating * Mock ClientHttpRequestFactory that creates requests by iterating
* over the list of expected {@link DefaultRequestExpectation}'s. * over the list of expected {@link DefaultRequestExpectation}'s.
*/ */
private class MockClientHttpRequestFactory implements ClientHttpRequestFactory, org.springframework.http.client.AsyncClientHttpRequestFactory { private class MockClientHttpRequestFactory implements ClientHttpRequestFactory,
org.springframework.http.client.AsyncClientHttpRequestFactory {
@Override @Override
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) { public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) {

View File

@ -41,13 +41,13 @@ public class UnorderedRequestExpectationManager extends AbstractRequestExpectati
@Override @Override
public ClientHttpResponse validateRequestInternal(ClientHttpRequest request) throws IOException { public ClientHttpResponse validateRequestInternal(ClientHttpRequest request) throws IOException {
RequestExpectation expectation = this.remainingExpectations.findExpectation(request); RequestExpectation expectation = this.remainingExpectations.findExpectation(request);
if (expectation != null) { if (expectation == null) {
throw createUnexpectedRequestError(request);
}
ClientHttpResponse response = expectation.createResponse(request); ClientHttpResponse response = expectation.createResponse(request);
this.remainingExpectations.update(expectation); this.remainingExpectations.update(expectation);
return response; return response;
} }
throw createUnexpectedRequestError(request);
}
@Override @Override
public void reset() { public void reset() {

View File

@ -28,14 +28,16 @@ import org.springframework.http.client.ClientHttpRequest;
import static junit.framework.TestCase.assertFalse; import static junit.framework.TestCase.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.springframework.http.HttpMethod.*; import static org.springframework.http.HttpMethod.GET;
import static org.springframework.test.web.client.ExpectedCount.*; import static org.springframework.http.HttpMethod.POST;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.*; import static org.springframework.test.web.client.ExpectedCount.once;
import static org.springframework.test.web.client.response.MockRestResponseCreators.*; import static org.springframework.test.web.client.ExpectedCount.twice;
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}. * Unit tests for {@link DefaultRequestExpectation}.
*
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
*/ */
public class DefaultRequestExpectationTests { public class DefaultRequestExpectationTests {

View File

@ -21,12 +21,11 @@ import org.junit.Test;
import org.springframework.test.web.client.MockRestServiceServer.MockRestServiceServerBuilder; import org.springframework.test.web.client.MockRestServiceServer.MockRestServiceServerBuilder;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.*; import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
import static org.springframework.test.web.client.response.MockRestResponseCreators.*; import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
/** /**
* Unit tests for {@link MockRestServiceServer}. * Unit tests for {@link MockRestServiceServer}.
*
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
*/ */
public class MockRestServiceServerTests { public class MockRestServiceServerTests {

View File

@ -27,15 +27,20 @@ import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpRequest; import org.springframework.http.client.ClientHttpRequest;
import org.springframework.mock.http.client.MockClientHttpRequest; import org.springframework.mock.http.client.MockClientHttpRequest;
import static org.junit.Assert.*; import static org.junit.Assert.assertEquals;
import static org.springframework.http.HttpMethod.*; import static org.springframework.http.HttpMethod.GET;
import static org.springframework.test.web.client.ExpectedCount.*; import static org.springframework.http.HttpMethod.POST;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.*; import static org.springframework.test.web.client.ExpectedCount.max;
import static org.springframework.test.web.client.response.MockRestResponseCreators.*; 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.ExpectedCount.twice;
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 SimpleRequestExpectationManager}. * Unit tests for {@link SimpleRequestExpectationManager}.
*
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
*/ */
public class SimpleRequestExpectationManagerTests { public class SimpleRequestExpectationManagerTests {