Browse Source
This commit factors out the logic to declare and manage expectations including matching them to requests and verifying at the end behind a commong abstraction. MockRestServiceServer delegates to the new abstraction and is no longer aware of how that's done. There are two implementations, one for ordered and another for unordered expectation. Issue: SPR-11365pull/965/merge
9 changed files with 464 additions and 167 deletions
@ -0,0 +1,79 @@
@@ -0,0 +1,79 @@
|
||||
/* |
||||
* 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.io.IOException; |
||||
import java.util.LinkedList; |
||||
import java.util.List; |
||||
|
||||
import org.springframework.http.client.ClientHttpRequest; |
||||
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. |
||||
* |
||||
* <p>Sub-classes are responsible for matching actual to expected requests and |
||||
* for verifying remaining expectations at the end. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
* @since 4.3 |
||||
*/ |
||||
public abstract class AbstractRequestExpectationManager implements RequestExpectationManager { |
||||
|
||||
private final List<RequestExpectation> expectations = new LinkedList<RequestExpectation>(); |
||||
|
||||
private final List<ClientHttpRequest> requests = new LinkedList<ClientHttpRequest>(); |
||||
|
||||
|
||||
public AbstractRequestExpectationManager() { |
||||
} |
||||
|
||||
public AbstractRequestExpectationManager(List<RequestExpectation> expectations) { |
||||
this.expectations.addAll(expectations); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public List<RequestExpectation> getExpectations() { |
||||
return this.expectations; |
||||
} |
||||
|
||||
@Override |
||||
public List<ClientHttpRequest> 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); |
||||
getExpectations().add(expectation); |
||||
return expectation; |
||||
} |
||||
|
||||
@Override |
||||
public ClientHttpResponse validateRequest(ClientHttpRequest request) throws IOException { |
||||
ClientHttpResponse response = validateRequestInternal(request); |
||||
getRequests().add(request); |
||||
return response; |
||||
} |
||||
|
||||
protected abstract ClientHttpResponse validateRequestInternal(ClientHttpRequest request) |
||||
throws IOException; |
||||
|
||||
} |
||||
@ -0,0 +1,78 @@
@@ -0,0 +1,78 @@
|
||||
/* |
||||
* 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.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; |
||||
|
||||
/** |
||||
* {@code RequestExpectationManager} that expects requests to follow the order |
||||
* in which expected requests were declared. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
* @since 4.3 |
||||
*/ |
||||
public class OrderedRequestExpectationManager extends AbstractRequestExpectationManager { |
||||
|
||||
private Iterator<RequestExpectation> iterator; |
||||
|
||||
|
||||
@Override |
||||
public ClientHttpResponse validateRequestInternal(ClientHttpRequest request) throws IOException { |
||||
if (this.iterator == null) { |
||||
this.iterator = getExpectations().iterator(); |
||||
} |
||||
if (!this.iterator.hasNext()) { |
||||
HttpMethod method = request.getMethod(); |
||||
URI uri = request.getURI(); |
||||
throw new AssertionError("No further requests expected: HTTP " + method + " " + uri); |
||||
} |
||||
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(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(" 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"); |
||||
} |
||||
} |
||||
return sb.toString(); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,27 @@
@@ -0,0 +1,27 @@
|
||||
/* |
||||
* 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; |
||||
|
||||
/** |
||||
* A contract that combines {@code RequestMatcher} with {@code ResponseCreator} |
||||
* to define an expected request and a response to use for it. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
* @since 4.3 |
||||
*/ |
||||
public interface RequestExpectation extends RequestMatcher, ResponseCreator { |
||||
|
||||
} |
||||
@ -0,0 +1,68 @@
@@ -0,0 +1,68 @@
|
||||
/* |
||||
* 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.io.IOException; |
||||
import java.util.List; |
||||
|
||||
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. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
* @since 4.3 |
||||
*/ |
||||
public interface RequestExpectationManager { |
||||
|
||||
/** |
||||
* Return the list of declared request expectations. |
||||
*/ |
||||
List<RequestExpectation> getExpectations(); |
||||
|
||||
/** |
||||
* Return the list of actual requests. |
||||
*/ |
||||
List<ClientHttpRequest> getRequests(); |
||||
|
||||
/** |
||||
* Set up a new request expectation. The returned {@link ResponseActions} is |
||||
* used to add more expectations and define a response. |
||||
* @param requestMatcher a request expectation |
||||
* @return for setting up further expectations and define a response |
||||
*/ |
||||
ResponseActions expectRequest(RequestMatcher requestMatcher); |
||||
|
||||
/** |
||||
* Validate the given actual request against the declared expectations |
||||
* raising {@link AssertionError} if not met. |
||||
* @param request the request |
||||
* @return the response to return if the request was validated. |
||||
* @throws AssertionError when some expectations were not met |
||||
* @throws IOException |
||||
*/ |
||||
ClientHttpResponse validateRequest(ClientHttpRequest request) throws IOException; |
||||
|
||||
/** |
||||
* Verify that all expectations have been met. |
||||
* @throws AssertionError when some expectations were not met |
||||
*/ |
||||
void verify(); |
||||
|
||||
} |
||||
@ -0,0 +1,93 @@
@@ -0,0 +1,93 @@
|
||||
/* |
||||
* 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.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 tries to match actual requests to |
||||
* expected requests regardless of the order in which expected requests were |
||||
* declared. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
* @since 4.3 |
||||
*/ |
||||
public class UnorderedRequestExpectationManager extends AbstractRequestExpectationManager { |
||||
|
||||
private final List<RequestExpectation> remainingExpectations = new LinkedList<RequestExpectation>(); |
||||
|
||||
|
||||
public UnorderedRequestExpectationManager() { |
||||
} |
||||
|
||||
public UnorderedRequestExpectationManager(List<RequestExpectation> expectations) { |
||||
super(expectations); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public ClientHttpResponse validateRequestInternal(ClientHttpRequest request) throws IOException { |
||||
if (getRequests().isEmpty()) { |
||||
this.remainingExpectations.addAll(getExpectations()); |
||||
} |
||||
for (RequestExpectation expectation : getExpectations()) { |
||||
try { |
||||
expectation.match(request); |
||||
this.remainingExpectations.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(); |
||||
} |
||||
|
||||
} |
||||
@ -1,100 +0,0 @@
@@ -1,100 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2014 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.test.web.client; |
||||
|
||||
import java.net.URI; |
||||
|
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
|
||||
import org.springframework.http.HttpMethod; |
||||
import org.springframework.http.client.ClientHttpRequest; |
||||
import org.springframework.http.client.ClientHttpRequestFactory; |
||||
import org.springframework.web.client.RestTemplate; |
||||
|
||||
import static org.junit.Assert.*; |
||||
import static org.springframework.test.web.client.match.MockRestRequestMatchers.*; |
||||
|
||||
/** |
||||
* Tests for |
||||
* {@link org.springframework.test.web.client.MockMvcClientHttpRequestFactory}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class MockClientHttpRequestFactoryTests { |
||||
|
||||
private MockRestServiceServer server; |
||||
|
||||
private ClientHttpRequestFactory factory; |
||||
|
||||
|
||||
@Before |
||||
public void setup() { |
||||
RestTemplate restTemplate = new RestTemplate(); |
||||
this.server = MockRestServiceServer.createServer(restTemplate); |
||||
this.factory = restTemplate.getRequestFactory(); |
||||
} |
||||
|
||||
@Test |
||||
public void createRequest() throws Exception { |
||||
URI uri = new URI("/foo"); |
||||
ClientHttpRequest actual = this.factory.createRequest(uri, HttpMethod.GET); |
||||
|
||||
assertEquals(uri, actual.getURI()); |
||||
assertEquals(HttpMethod.GET, actual.getMethod()); |
||||
} |
||||
|
||||
@Test |
||||
public void noFurtherRequestsExpected() throws Exception { |
||||
try { |
||||
this.factory.createRequest(new URI("/foo"), HttpMethod.GET); |
||||
} |
||||
catch (AssertionError error) { |
||||
assertEquals("No further requests expected: HTTP GET /foo", error.getMessage()); |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
public void verifyZeroExpected() throws Exception { |
||||
this.server.verify(); |
||||
} |
||||
|
||||
@Test |
||||
public void verifyExpectedEqualExecuted() throws Exception { |
||||
this.server.expect(anything()); |
||||
this.server.expect(anything()); |
||||
|
||||
this.factory.createRequest(new URI("/foo"), HttpMethod.GET); |
||||
this.factory.createRequest(new URI("/bar"), HttpMethod.POST); |
||||
} |
||||
|
||||
@Test |
||||
public void verifyMoreExpected() throws Exception { |
||||
this.server.expect(anything()); |
||||
this.server.expect(anything()); |
||||
|
||||
this.factory.createRequest(new URI("/foo"), HttpMethod.GET); |
||||
|
||||
try { |
||||
this.server.verify(); |
||||
} |
||||
catch (AssertionError error) { |
||||
assertTrue(error.getMessage(), error.getMessage().contains("1 out of 2 were executed")); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,90 @@
@@ -0,0 +1,90 @@
|
||||
/* |
||||
* 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.Test; |
||||
|
||||
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.test.web.client.response.MockRestResponseCreators.withSuccess; |
||||
|
||||
/** |
||||
* Unit tests for {@link AbstractRequestExpectationManager}. |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class OrderedRequestExpectationManagerTests { |
||||
|
||||
private OrderedRequestExpectationManager manager = new OrderedRequestExpectationManager(); |
||||
|
||||
|
||||
@Test |
||||
public void validateWithUnexpectedRequest() throws Exception { |
||||
try { |
||||
this.manager.validateRequest(request(HttpMethod.GET, "/foo")); |
||||
} |
||||
catch (AssertionError error) { |
||||
assertEquals("No further requests expected: HTTP GET /foo", 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")); |
||||
this.manager.verify(); |
||||
} |
||||
|
||||
@Test |
||||
public void verifyWithZeroExpectations() throws Exception { |
||||
this.manager.verify(); |
||||
} |
||||
|
||||
@Test |
||||
public void verifyWithRemainingExpectations() throws Exception { |
||||
this.manager.expectRequest(anything()).andRespond(withSuccess()); |
||||
this.manager.expectRequest(anything()).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")); |
||||
} |
||||
} |
||||
|
||||
private ClientHttpRequest request(HttpMethod method, String url) { |
||||
try { |
||||
return new MockAsyncClientHttpRequest(method, new URI(url)); |
||||
} |
||||
catch (URISyntaxException ex) { |
||||
throw new IllegalStateException(ex); |
||||
} |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue