Fold spring-test-mvc sources into spring-test

With spring-test compiling against Servlet 3.0 it is no longer required
to compile Spring MVC Test sources separately (from spring-test).
This commit is contained in:
Rossen Stoyanchev
2013-11-05 11:44:13 -05:00
parent 0eeb6717e0
commit 2e57cf8bfc
137 changed files with 27 additions and 48 deletions

View File

@@ -0,0 +1,148 @@
/*
* Copyright 2002-2012 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.util;
import static org.springframework.test.util.AssertionErrors.assertEquals;
import static org.springframework.test.util.AssertionErrors.assertTrue;
import static org.springframework.test.util.AssertionErrors.fail;
import static org.springframework.test.util.MatcherAssertionErrors.assertThat;
import java.text.ParseException;
import java.util.List;
import org.hamcrest.Matcher;
import com.jayway.jsonpath.InvalidPathException;
import com.jayway.jsonpath.JsonPath;
/**
* A helper class for applying assertions via JSONPath expressions.
*
* @author Rossen Stoyanchev
* @since 3.2
*/
public class JsonPathExpectationsHelper {
private final String expression;
private final JsonPath jsonPath;
/**
* Class constructor.
*
* @param expression the JSONPath expression
* @param args arguments to parameterize the JSONPath expression with using the
* formatting specifiers defined in {@link String#format(String, Object...)}
*/
public JsonPathExpectationsHelper(String expression, Object ... args) {
this.expression = String.format(expression, args);
this.jsonPath = JsonPath.compile(this.expression);
}
/**
* Evaluate the JSONPath and assert the resulting value with the given {@code Matcher}.
* @param content the response content
* @param matcher the matcher to assert on the resulting json path
*/
@SuppressWarnings("unchecked")
public <T> void assertValue(String content, Matcher<T> matcher) throws ParseException {
T value = (T) evaluateJsonPath(content);
assertThat("JSON path" + this.expression, value, matcher);
}
private Object evaluateJsonPath(String content) throws ParseException {
String message = "No value for JSON path: " + this.expression + ", exception: ";
try {
return this.jsonPath.read(content);
}
catch (InvalidPathException ex) {
throw new AssertionError(message + ex.getMessage());
}
catch (ArrayIndexOutOfBoundsException ex) {
throw new AssertionError(message + ex.getMessage());
}
catch (IndexOutOfBoundsException ex) {
throw new AssertionError(message + ex.getMessage());
}
}
/**
* Apply the JSONPath and assert the resulting value.
*/
public void assertValue(String responseContent, Object expectedValue) throws ParseException {
Object actualValue = evaluateJsonPath(responseContent);
if ((actualValue instanceof List) && !(expectedValue instanceof List)) {
@SuppressWarnings("rawtypes")
List actualValueList = (List) actualValue;
if (actualValueList.size() == 0) {
fail("No matching value for JSON path \"" + this.expression + "\"");
}
if (actualValueList.size() != 1) {
fail("Got a list of values " + actualValue + " instead of the value " + expectedValue);
}
actualValue = actualValueList.get(0);
}
else if (actualValue != null && expectedValue != null) {
assertEquals("For JSON path " + this.expression + " type of value",
expectedValue.getClass(), actualValue.getClass());
}
assertEquals("JSON path" + this.expression, expectedValue, actualValue);
}
/**
* Apply the JSONPath and assert the resulting value is an array.
*/
public void assertValueIsArray(String responseContent) throws ParseException {
Object actualValue = evaluateJsonPath(responseContent);
assertTrue("No value for JSON path \"" + this.expression + "\"", actualValue != null);
String reason = "Expected array at JSON path " + this.expression + " but found " + actualValue;
assertTrue(reason, actualValue instanceof List);
}
/**
* Evaluate the JSON path and assert the resulting content exists.
*/
public void exists(String content) throws ParseException {
Object value = evaluateJsonPath(content);
String reason = "No value for JSON path " + this.expression;
assertTrue(reason, value != null);
if (List.class.isInstance(value)) {
assertTrue(reason, !((List<?>) value).isEmpty());
}
}
/**
* Evaluate the JSON path and assert it doesn't point to any content.
*/
public void doesNotExist(String content) throws ParseException {
Object value;
try {
value = evaluateJsonPath(content);
}
catch (AssertionError ex) {
return;
}
String reason = String.format("Expected no value for JSON path: %s but found: %s", this.expression, value);
if (List.class.isInstance(value)) {
assertTrue(reason, ((List<?>) value).isEmpty());
}
else {
assertTrue(reason, value == null);
}
}
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright 2002-2012 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.util;
import java.lang.reflect.Method;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.StringDescription;
import org.springframework.util.ClassUtils;
/**
* A replacement of {@link org.hamcrest.MatcherAssert} that removes the need to
* depend on "hamcrest-all" when using Hamcrest 1.1 and also maintains backward
* compatibility with Hamcrest 1.1 (also embedded in JUnit 4.4 through 4.8).
*
* @author Rossen Stoyanchev
* @since 3.2
*/
public abstract class MatcherAssertionErrors {
private static final Method describeMismatchMethod =
ClassUtils.getMethodIfAvailable(Matcher.class, "describeMismatch", Object.class, Description.class);
private MatcherAssertionErrors() {
}
/**
* Asserts that the given matcher matches the actual value.
*
* @param <T> the static type accepted by the matcher
* @param actual the value to match against
* @param matcher the matcher
*/
public static <T> void assertThat(T actual, Matcher<T> matcher) {
assertThat("", actual, matcher);
}
/**
* Asserts that the given matcher matches the actual value.
*
* @param <T> the static type accepted by the matcher
* @param reason additional information about the error
* @param actual the value to match against
* @param matcher the matcher
*/
public static <T> void assertThat(String reason, T actual, Matcher<T> matcher) {
if (!matcher.matches(actual)) {
Description description = new StringDescription();
description.appendText(reason);
description.appendText("\nExpected: ");
description.appendDescriptionOf(matcher);
if (describeMismatchMethod != null) {
description.appendText("\n but: ");
matcher.describeMismatch(actual, description);
}
else {
description.appendText("\n got: ");
description.appendValue(actual);
description.appendText("\n");
}
throw new AssertionError(description.toString());
}
}
}

View File

@@ -0,0 +1,100 @@
/*
* Copyright 2002-2012 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.util;
import static org.springframework.test.util.MatcherAssertionErrors.assertThat;
import java.io.StringReader;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Source;
import javax.xml.transform.dom.DOMSource;
import org.custommonkey.xmlunit.Diff;
import org.custommonkey.xmlunit.XMLUnit;
import org.hamcrest.Matcher;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
/**
* A helper class for assertions on XML content.
*
* @author Rossen Stoyanchev
* @since 3.2
*/
public class XmlExpectationsHelper {
/**
* Parse the content as {@link Node} and apply a {@link Matcher}.
*/
public void assertNode(String content, Matcher<? super Node> matcher) throws Exception {
Document document = parseXmlString(content);
assertThat("Body content", document, matcher);
}
private Document parseXmlString(String xml) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
DocumentBuilder documentBuilder = factory.newDocumentBuilder();
InputSource inputSource = new InputSource(new StringReader(xml));
Document document = documentBuilder.parse(inputSource);
return document;
}
/**
* Parse the content as {@link DOMSource} and apply a {@link Matcher}.
* @see <a href="http://code.google.com/p/xml-matchers/">xml-matchers</a>
*/
public void assertSource(String content, Matcher<? super Source> matcher) throws Exception {
Document document = parseXmlString(content);
assertThat("Body content", new DOMSource(document), matcher);
}
/**
* Parse the expected and actual content strings as XML and assert that the
* two are "similar" -- i.e. they contain the same elements and attributes
* regardless of order.
*
* <p>Use of this method assumes the
* <a href="http://xmlunit.sourceforge.net/">XMLUnit<a/> library is available.
*
* @param expected the expected XML content
* @param actual the actual XML content
*
* @see MockMvcResultMatchers#xpath(String, Object...)
* @see MockMvcResultMatchers#xpath(String, Map, Object...)
*/
public void assertXmlEqual(String expected, String actual) throws Exception {
XMLUnit.setIgnoreWhitespace(true);
XMLUnit.setIgnoreComments(true);
XMLUnit.setIgnoreAttributeOrder(true);
Document control = XMLUnit.buildControlDocument(expected);
Document test = XMLUnit.buildTestDocument(actual);
Diff diff = new Diff(control, test);
if (!diff.similar()) {
AssertionErrors.fail("Body content " + diff.toString());
}
}
}

View File

@@ -0,0 +1,225 @@
/*
* Copyright 2002-2012 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.util;
import java.io.StringReader;
import java.util.Collections;
import java.util.Map;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.hamcrest.Matcher;
import org.springframework.util.CollectionUtils;
import org.springframework.util.xml.SimpleNamespaceContext;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import static org.springframework.test.util.AssertionErrors.*;
import static org.springframework.test.util.MatcherAssertionErrors.*;
/**
* A helper class for applying assertions via XPath expressions.
*
* @author Rossen Stoyanchev
* @since 3.2
*/
public class XpathExpectationsHelper {
private final String expression;
private final XPathExpression xpathExpression;
private final boolean hasNamespaces;
/**
* Class constructor.
*
* @param expression the XPath expression
* @param namespaces XML namespaces referenced in the XPath expression, or {@code null}
* @param args arguments to parameterize the XPath expression with using the
* formatting specifiers defined in {@link String#format(String, Object...)}
* @throws XPathExpressionException
*/
public XpathExpectationsHelper(String expression, Map<String, String> namespaces, Object... args)
throws XPathExpressionException {
this.expression = String.format(expression, args);
this.xpathExpression = compileXpathExpression(this.expression, namespaces);
this.hasNamespaces = !CollectionUtils.isEmpty(namespaces);
}
private XPathExpression compileXpathExpression(String expression, Map<String, String> namespaces)
throws XPathExpressionException {
SimpleNamespaceContext namespaceContext = new SimpleNamespaceContext();
namespaceContext.setBindings((namespaces != null) ? namespaces : Collections.<String, String> emptyMap());
XPath xpath = XPathFactory.newInstance().newXPath();
xpath.setNamespaceContext(namespaceContext);
return xpath.compile(expression);
}
/**
* @return the compiled XPath expression.
*/
protected XPathExpression getXpathExpression() {
return this.xpathExpression;
}
/**
* Parse the content, evaluate the XPath expression as a {@link Node}, and
* assert it with the given {@code Matcher<Node>}.
*/
public void assertNode(String content, final Matcher<? super Node> matcher) throws Exception {
Document document = parseXmlString(content);
Node node = evaluateXpath(document, XPathConstants.NODE, Node.class);
assertThat("XPath " + this.expression, node, matcher);
}
/**
* Parse the given XML content to a {@link Document}.
*
* @param xml the content to parse
* @return the parsed document
* @throws Exception in case of errors
*/
protected Document parseXmlString(String xml) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(this.hasNamespaces);
DocumentBuilder documentBuilder = factory.newDocumentBuilder();
InputSource inputSource = new InputSource(new StringReader(xml));
Document document = documentBuilder.parse(inputSource);
return document;
}
/**
* Apply the XPath expression to given document.
* @throws XPathExpressionException
*/
@SuppressWarnings("unchecked")
protected <T> T evaluateXpath(Document document, QName evaluationType, Class<T> expectedClass)
throws XPathExpressionException {
return (T) getXpathExpression().evaluate(document, evaluationType);
}
/**
* Apply the XPath expression and assert the resulting content exists.
* @throws Exception if content parsing or expression evaluation fails
*/
public void exists(String content) throws Exception {
Document document = parseXmlString(content);
Node node = evaluateXpath(document, XPathConstants.NODE, Node.class);
assertTrue("XPath " + this.expression + " does not exist", node != null);
}
/**
* Apply the XPath expression and assert the resulting content does not exist.
* @throws Exception if content parsing or expression evaluation fails
*/
public void doesNotExist(String content) throws Exception {
Document document = parseXmlString(content);
Node node = evaluateXpath(document, XPathConstants.NODE, Node.class);
assertTrue("XPath " + this.expression + " exists", node == null);
}
/**
* Apply the XPath expression and assert the resulting content with the
* given Hamcrest matcher.
*
* @throws Exception if content parsing or expression evaluation fails
*/
public void assertNodeCount(String content, Matcher<Integer> matcher) throws Exception {
Document document = parseXmlString(content);
NodeList nodeList = evaluateXpath(document, XPathConstants.NODESET, NodeList.class);
assertThat("nodeCount for XPath " + this.expression, nodeList.getLength(), matcher);
}
/**
* Apply the XPath expression and assert the resulting content as an integer.
* @throws Exception if content parsing or expression evaluation fails
*/
public void assertNodeCount(String content, int expectedCount) throws Exception {
Document document = parseXmlString(content);
NodeList nodeList = evaluateXpath(document, XPathConstants.NODESET, NodeList.class);
assertEquals("nodeCount for XPath " + this.expression, expectedCount, nodeList.getLength());
}
/**
* Apply the XPath expression and assert the resulting content with the
* given Hamcrest matcher.
*
* @throws Exception if content parsing or expression evaluation fails
*/
public void assertString(String content, Matcher<? super String> matcher) throws Exception {
Document document = parseXmlString(content);
String result = evaluateXpath(document, XPathConstants.STRING, String.class);
assertThat("XPath " + this.expression, result, matcher);
}
/**
* Apply the XPath expression and assert the resulting content as a String.
* @throws Exception if content parsing or expression evaluation fails
*/
public void assertString(String content, String expectedValue) throws Exception {
Document document = parseXmlString(content);
String actual = evaluateXpath(document, XPathConstants.STRING, String.class);
assertEquals("XPath " + this.expression, expectedValue, actual);
}
/**
* Apply the XPath expression and assert the resulting content with the
* given Hamcrest matcher.
*
* @throws Exception if content parsing or expression evaluation fails
*/
public void assertNumber(String content, Matcher<? super Double> matcher) throws Exception {
Document document = parseXmlString(content);
Double result = evaluateXpath(document, XPathConstants.NUMBER, Double.class);
assertThat("XPath " + this.expression, result, matcher);
}
/**
* Apply the XPath expression and assert the resulting content as a Double.
* @throws Exception if content parsing or expression evaluation fails
*/
public void assertNumber(String content, Double expectedValue) throws Exception {
Document document = parseXmlString(content);
Double actual = evaluateXpath(document, XPathConstants.NUMBER, Double.class);
assertEquals("XPath " + this.expression, expectedValue, actual);
}
/**
* Apply the XPath expression and assert the resulting content as a Boolean.
* @throws Exception if content parsing or expression evaluation fails
*/
public void assertBoolean(String content, boolean expectedValue) throws Exception {
Document document = parseXmlString(content);
String actual = evaluateXpath(document, XPathConstants.STRING, String.class);
assertEquals("XPath " + this.expression, expectedValue, Boolean.parseBoolean(actual));
}
}

View File

@@ -0,0 +1,94 @@
/*
* Copyright 2002-2012 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 static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.request;
import java.io.IOException;
import java.net.URI;
import java.util.List;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.mock.http.client.MockClientHttpRequest;
import org.springframework.mock.http.client.MockClientHttpResponse;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
/**
* A {@link ClientHttpRequestFactory} for requests executed via {@link MockMvc}.
*
* @author Rossen Stoyanchev
* @since 3.2
*/
public class MockMvcClientHttpRequestFactory implements ClientHttpRequestFactory {
private final MockMvc mockMvc;
public MockMvcClientHttpRequestFactory(MockMvc mockMvc) {
this.mockMvc = mockMvc;
}
@Override
public ClientHttpRequest createRequest(final URI uri, final HttpMethod httpMethod) throws IOException {
return new MockClientHttpRequest(httpMethod, uri) {
@Override
public ClientHttpResponse executeInternal() throws IOException {
try {
MockHttpServletRequestBuilder requestBuilder = request(httpMethod, uri.toString());
requestBuilder.content(getBodyAsBytes());
requestBuilder.headers(getHeaders());
MvcResult mvcResult = MockMvcClientHttpRequestFactory.this.mockMvc.perform(requestBuilder).andReturn();
MockHttpServletResponse servletResponse = mvcResult.getResponse();
HttpStatus status = HttpStatus.valueOf(servletResponse.getStatus());
byte[] body = servletResponse.getContentAsByteArray();
HttpHeaders headers = getResponseHeaders(servletResponse);
MockClientHttpResponse clientResponse = new MockClientHttpResponse(body, status);
clientResponse.getHeaders().putAll(headers);
return clientResponse;
}
catch (Exception ex) {
byte[] body = ex.toString().getBytes("UTF-8");
return new MockClientHttpResponse(body, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
};
}
private HttpHeaders getResponseHeaders(MockHttpServletResponse response) {
HttpHeaders headers = new HttpHeaders();
for (String name : response.getHeaderNames()) {
List<String> values = response.getHeaders(name);
for (String value : values) {
headers.add(name, value);
}
}
return headers;
}
}

View File

@@ -0,0 +1,209 @@
/*
* Copyright 2002-2012 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 java.util.LinkedList;
import java.util.List;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.test.web.client.match.MockRestRequestMatchers;
import org.springframework.test.web.client.response.MockRestResponseCreators;
import org.springframework.util.Assert;
import org.springframework.web.client.RestTemplate;
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.
*
* <p>Below is an example:
* <pre class="code">
* RestTemplate restTemplate = new RestTemplate()
* MockRestServiceServer mockServer = MockRestServiceServer.createServer(restTemplate);
*
* mockServer.expect(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);
* &#47;&#47; Use the hotel instance...
*
* mockServer.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><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}.
*
* @author Craig Walls
* @author Rossen Stoyanchev
* @since 3.2
*/
public class MockRestServiceServer {
private final List<RequestMatcherClientHttpRequest> expectedRequests =
new LinkedList<RequestMatcherClientHttpRequest>();
private final List<RequestMatcherClientHttpRequest> actualRequests =
new LinkedList<RequestMatcherClientHttpRequest>();
/**
* Private constructor.
* @see #createServer(RestTemplate)
* @see #createServer(RestGatewaySupport)
*/
private MockRestServiceServer() {
}
/**
* Create a {@code MockRestServiceServer} and set up the given
* {@code RestTemplate} with a mock {@link ClientHttpRequestFactory}.
*
* @param restTemplate the RestTemplate to set up for mock testing
* @return the created mock server
*/
public static MockRestServiceServer createServer(RestTemplate restTemplate) {
Assert.notNull(restTemplate, "'restTemplate' must not be null");
MockRestServiceServer mockServer = new MockRestServiceServer();
RequestMatcherClientHttpRequestFactory factory = mockServer.new RequestMatcherClientHttpRequestFactory();
restTemplate.setRequestFactory(factory);
return mockServer;
}
/**
* Create a {@code MockRestServiceServer} and set up the given
* {@code RestGatewaySupport} with a mock {@link ClientHttpRequestFactory}.
*
* @param restGateway the REST gateway to set up for mock testing
* @return the created mock server
*/
public static MockRestServiceServer createServer(RestGatewaySupport restGateway) {
Assert.notNull(restGateway, "'gatewaySupport' must not be null");
return createServer(restGateway.getRestTemplate());
}
/**
* 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 requestMatcher a request expectation, see {@link MockRestRequestMatchers}
* @return used to set up further expectations or to define a response
*/
public ResponseActions expect(RequestMatcher requestMatcher) {
Assert.state(this.actualRequests.isEmpty(), "Can't add more expected requests with test already underway");
RequestMatcherClientHttpRequest request = new RequestMatcherClientHttpRequest(requestMatcher);
this.expectedRequests.add(request);
return request;
}
/**
* Verify that all expected requests set up via
* {@link #expect(RequestMatcher)} were indeed performed.
*
* @throws AssertionError when some expectations were not met
*/
public void verify() {
if (this.expectedRequests.isEmpty() || this.expectedRequests.equals(this.actualRequests)) {
return;
}
throw new AssertionError(getVerifyMessage());
}
private String getVerifyMessage() {
StringBuilder sb = new StringBuilder("Further request(s) expected\n");
if (this.actualRequests.size() > 0) {
sb.append("The following ");
}
sb.append(this.actualRequests.size()).append(" out of ");
sb.append(this.expectedRequests.size()).append(" were executed");
if (this.actualRequests.size() > 0) {
sb.append(":\n");
for (RequestMatcherClientHttpRequest request : this.actualRequests) {
sb.append(request.toString()).append("\n");
}
}
return sb.toString();
}
/**
* Mock ClientHttpRequestFactory that creates requests by iterating
* over the list of expected {@link RequestMatcherClientHttpRequest}'s.
*/
private class RequestMatcherClientHttpRequestFactory implements ClientHttpRequestFactory {
private Iterator<RequestMatcherClientHttpRequest> requestIterator;
@Override
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
Assert.notNull(uri, "'uri' must not be null");
Assert.notNull(httpMethod, "'httpMethod' must not be null");
if (this.requestIterator == null) {
this.requestIterator = MockRestServiceServer.this.expectedRequests.iterator();
}
if (!this.requestIterator.hasNext()) {
throw new AssertionError("No further requests expected");
}
RequestMatcherClientHttpRequest request = this.requestIterator.next();
request.setURI(uri);
request.setMethod(httpMethod);
MockRestServiceServer.this.actualRequests.add(request);
return request;
}
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2002-2012 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 org.springframework.http.client.ClientHttpRequest;
/**
* A contract for matching requests to expectations.
*
* @author Craig Walls
* @since 3.2
*/
public interface RequestMatcher {
/**
* Match the given request against some expectations.
*
* @param request the request to make assertions on
* @throws IOException in case of I/O errors
* @throws AssertionError if expectations are not met
*/
void match(ClientHttpRequest request) throws IOException, AssertionError;
}

View File

@@ -0,0 +1,82 @@
/*
* Copyright 2002-2012 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.ClientHttpResponse;
import org.springframework.mock.http.client.MockClientHttpRequest;
import org.springframework.util.Assert;
/**
* A specialization of {@code MockClientHttpRequest} that matches the request
* against a set of expectations, via {@link RequestMatcher} instances. The
* expectations are checked when the request is executed. This class also uses a
* {@link ResponseCreator} to create the response.
*
* @author Craig Walls
* @author Rossen Stoyanchev
* @since 3.2
*/
class RequestMatcherClientHttpRequest extends MockClientHttpRequest implements ResponseActions {
private final List<RequestMatcher> requestMatchers = new LinkedList<RequestMatcher>();
private ResponseCreator responseCreator;
public RequestMatcherClientHttpRequest(RequestMatcher requestMatcher) {
Assert.notNull(requestMatcher, "RequestMatcher is required");
this.requestMatchers.add(requestMatcher);
}
@Override
public ResponseActions andExpect(RequestMatcher requestMatcher) {
Assert.notNull(requestMatcher, "RequestMatcher is required");
this.requestMatchers.add(requestMatcher);
return this;
}
@Override
public void andRespond(ResponseCreator responseCreator) {
Assert.notNull(responseCreator, "ResponseCreator is required");
this.responseCreator = responseCreator;
}
@Override
public ClientHttpResponse executeInternal() throws IOException {
if (this.requestMatchers.isEmpty()) {
throw new AssertionError("No request expectations to execute");
}
if (this.responseCreator == null) {
throw new AssertionError("No ResponseCreator was set up. Add it after request expectations, "
+ "e.g. MockRestServiceServer.expect(requestTo(\"/foo\")).andRespond(withSuccess())");
}
for (RequestMatcher requestMatcher : this.requestMatchers) {
requestMatcher.match(this);
}
setResponse(this.responseCreator.createResponse(this));
return super.executeInternal();
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2002-2012 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 for setting up request expectations and defining a response.
* Implementations can be obtained through {@link MockRestServiceServer#expect(RequestMatcher)}.
*
* @author Craig Walls
* @since 3.2
*/
public interface ResponseActions {
/**
* Add a request expectation.
* @return the expectation
*/
ResponseActions andExpect(RequestMatcher requestMatcher);
/**
* Define the response.
* @param responseCreator the creator of the response
*/
void andRespond(ResponseCreator responseCreator);
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2002-2012 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 org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.test.web.client.response.MockRestResponseCreators;
/**
* A contract for creating a {@link ClientHttpResponse}.
* Implementations can be obtained via {@link MockRestResponseCreators}.
*
* @author Craig Walls
* @since 3.2
*/
public interface ResponseCreator {
/**
* Create a response for the given request.
* @param request the request
*/
ClientHttpResponse createResponse(ClientHttpRequest request) throws IOException;
}

View File

@@ -0,0 +1,202 @@
/*
* Copyright 2002-2013 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.match;
import static org.springframework.test.util.AssertionErrors.assertEquals;
import static org.springframework.test.util.AssertionErrors.assertTrue;
import static org.springframework.test.util.MatcherAssertionErrors.assertThat;
import java.io.IOException;
import javax.xml.transform.Source;
import javax.xml.transform.dom.DOMSource;
import org.hamcrest.Matcher;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.mock.http.client.MockClientHttpRequest;
import org.springframework.test.util.XmlExpectationsHelper;
import org.springframework.test.web.client.RequestMatcher;
import org.w3c.dom.Node;
/**
* Factory for request content {@code RequestMatcher}'s. An instance of this
* class is typically accessed via {@link MockRestRequestMatchers#content()}.
*
* @author Rossen Stoyanchev
* @since 3.2
*/
public class ContentRequestMatchers {
private final XmlExpectationsHelper xmlHelper;
/**
* Class constructor, not for direct instantiation.
* Use {@link MockRestRequestMatchers#content()}.
*/
protected ContentRequestMatchers() {
this.xmlHelper = new XmlExpectationsHelper();
}
/**
* Assert the request content type as a String.
*/
public RequestMatcher contentType(String expectedContentType) {
return contentType(MediaType.parseMediaType(expectedContentType));
}
/**
* Assert the request content type as a {@link MediaType}.
*/
public RequestMatcher contentType(final MediaType expectedContentType) {
return new RequestMatcher() {
@Override
public void match(ClientHttpRequest request) throws IOException, AssertionError {
MediaType actualContentType = request.getHeaders().getContentType();
assertTrue("Content type not set", actualContentType != null);
assertEquals("Content type", expectedContentType, actualContentType);
}
};
}
/**
* Assert the request content type is compatible with the given
* content type as defined by {@link MediaType#isCompatibleWith(MediaType)}.
*/
public RequestMatcher contentTypeCompatibleWith(String contentType) {
return contentTypeCompatibleWith(MediaType.parseMediaType(contentType));
}
/**
* Assert the request content type is compatible with the given
* content type as defined by {@link MediaType#isCompatibleWith(MediaType)}.
*/
public RequestMatcher contentTypeCompatibleWith(final MediaType contentType) {
return new RequestMatcher() {
@Override
public void match(ClientHttpRequest request) throws IOException, AssertionError {
MediaType actualContentType = request.getHeaders().getContentType();
assertTrue("Content type not set", actualContentType != null);
assertTrue("Content type [" + actualContentType + "] is not compatible with [" + contentType + "]",
actualContentType.isCompatibleWith(contentType));
}
};
}
/**
* Get the body of the request as a UTF-8 string and appply the given {@link Matcher}.
*/
public RequestMatcher string(final Matcher<? super String> matcher) {
return new RequestMatcher() {
@Override
public void match(ClientHttpRequest request) throws IOException, AssertionError {
MockClientHttpRequest mockRequest = (MockClientHttpRequest) request;
assertThat("Request content", mockRequest.getBodyAsString(), matcher);
}
};
}
/**
* Get the body of the request as a UTF-8 string and compare it to the given String.
*/
public RequestMatcher string(final String expectedContent) {
return new RequestMatcher() {
@Override
public void match(ClientHttpRequest request) throws IOException, AssertionError {
MockClientHttpRequest mockRequest = (MockClientHttpRequest) request;
assertEquals("Request content", expectedContent, mockRequest.getBodyAsString());
}
};
}
/**
* Compare the body of the request to the given byte array.
*/
public RequestMatcher bytes(final byte[] expectedContent) {
return new RequestMatcher() {
@Override
public void match(ClientHttpRequest request) throws IOException, AssertionError {
MockClientHttpRequest mockRequest = (MockClientHttpRequest) request;
assertEquals("Request content", expectedContent, mockRequest.getBodyAsBytes());
}
};
}
/**
* Parse the request body and the given String as XML and assert that the
* two are "similar" - i.e. they contain the same elements and attributes
* regardless of order.
*
* <p>Use of this matcher assumes the
* <a href="http://xmlunit.sourceforge.net/">XMLUnit<a/> library is available.
*
* @param expectedXmlContent the expected XML content
*/
public RequestMatcher xml(final String expectedXmlContent) {
return new AbstractXmlRequestMatcher() {
@Override
protected void matchInternal(MockClientHttpRequest request) throws Exception {
xmlHelper.assertXmlEqual(expectedXmlContent, request.getBodyAsString());
}
};
}
/**
* Parse the request content as {@link Node} and apply the given {@link Matcher}.
*/
public RequestMatcher node(final Matcher<? super Node> matcher) {
return new AbstractXmlRequestMatcher() {
@Override
protected void matchInternal(MockClientHttpRequest request) throws Exception {
xmlHelper.assertNode(request.getBodyAsString(), matcher);
}
};
}
/**
* Parse the request content as {@link DOMSource} and apply the given {@link Matcher}.
* @see <a href="http://code.google.com/p/xml-matchers/">http://code.google.com/p/xml-matchers/</a>
*/
public RequestMatcher source(final Matcher<? super Source> matcher) {
return new AbstractXmlRequestMatcher() {
@Override
protected void matchInternal(MockClientHttpRequest request) throws Exception {
xmlHelper.assertSource(request.getBodyAsString(), matcher);
}
};
}
/**
* Abstract base class for XML {@link RequestMatcher}'s.
*/
private abstract static class AbstractXmlRequestMatcher implements RequestMatcher {
@Override
public final void match(ClientHttpRequest request) throws IOException, AssertionError {
try {
MockClientHttpRequest mockRequest = (MockClientHttpRequest) request;
matchInternal(mockRequest);
}
catch (Exception e) {
throw new AssertionError("Failed to parse expected or actual XML request content: " + e.getMessage());
}
}
protected abstract void matchInternal(MockClientHttpRequest request) throws Exception;
}
}

View File

@@ -0,0 +1,135 @@
/*
* Copyright 2002-2012 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.match;
import java.io.IOException;
import java.text.ParseException;
import org.hamcrest.Matcher;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.mock.http.client.MockClientHttpRequest;
import org.springframework.test.util.JsonPathExpectationsHelper;
import org.springframework.test.web.client.RequestMatcher;
/**
* Factory methods for request content {@code RequestMatcher}'s using a <a
* href="http://goessner.net/articles/JsonPath/">JSONPath</a> expression.
* An instance of this class is typically accessed via
* {@code RequestMatchers.jsonPath(..)}.
*
* @author Rossen Stoyanchev
* @since 3.2
*/
public class JsonPathRequestMatchers {
private JsonPathExpectationsHelper jsonPathHelper;
/**
* Class constructor, not for direct instantiation. Use
* {@link MockRestRequestMatchers#jsonPath(String, Matcher)} or
* {@link MockRestRequestMatchers#jsonPath(String, Object...)}.
*
* @param expression the JSONPath expression
* @param args arguments to parameterize the JSONPath expression with using
* the formatting specifiers defined in
* {@link String#format(String, Object...)}
*/
protected JsonPathRequestMatchers(String expression, Object ... args) {
this.jsonPathHelper = new JsonPathExpectationsHelper(expression, args);
}
/**
* Evaluate the JSONPath and assert the resulting value with the given {@code Matcher}.
*/
public <T> RequestMatcher value(final Matcher<T> matcher) {
return new AbstractJsonPathRequestMatcher() {
@Override
protected void matchInternal(MockClientHttpRequest request) throws IOException, ParseException {
jsonPathHelper.assertValue(request.getBodyAsString(), matcher);
}
};
}
/**
* Apply the JSONPath and assert the resulting value.
*/
public RequestMatcher value(final Object expectedValue) {
return new AbstractJsonPathRequestMatcher() {
@Override
protected void matchInternal(MockClientHttpRequest request) throws IOException, ParseException {
jsonPathHelper.assertValue(request.getBodyAsString(), expectedValue);
}
};
}
/**
* Apply the JSONPath and assert the resulting value.
*/
public RequestMatcher exists() {
return new AbstractJsonPathRequestMatcher() {
@Override
protected void matchInternal(MockClientHttpRequest request) throws IOException, ParseException {
jsonPathHelper.exists(request.getBodyAsString());
}
};
}
/**
* Evaluate the JSON path and assert the resulting content exists.
*/
public RequestMatcher doesNotExist() {
return new AbstractJsonPathRequestMatcher() {
@Override
protected void matchInternal(MockClientHttpRequest request) throws IOException, ParseException {
jsonPathHelper.doesNotExist(request.getBodyAsString());
}
};
}
/**
* Assert the content at the given JSONPath is an array.
*/
public RequestMatcher isArray() {
return new AbstractJsonPathRequestMatcher() {
@Override
protected void matchInternal(MockClientHttpRequest request) throws IOException, ParseException {
jsonPathHelper.assertValueIsArray(request.getBodyAsString());
}
};
}
/**
* Abstract base class for JSONPath {@link RequestMatcher}'s.
*/
private abstract static class AbstractJsonPathRequestMatcher implements RequestMatcher {
@Override
public final void match(ClientHttpRequest request) throws IOException, AssertionError {
try {
MockClientHttpRequest mockRequest = (MockClientHttpRequest) request;
matchInternal(mockRequest);
}
catch (ParseException e) {
throw new AssertionError("Failed to parse JSON request content: " + e.getMessage());
}
}
protected abstract void matchInternal(MockClientHttpRequest request) throws IOException, ParseException;
}
}

View File

@@ -0,0 +1,233 @@
/*
* Copyright 2002-2012 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.match;
import static org.springframework.test.util.MatcherAssertionErrors.assertThat;
import static org.springframework.test.util.AssertionErrors.*;
import java.io.IOException;
import java.net.URI;
import java.util.List;
import java.util.Map;
import javax.xml.xpath.XPathExpressionException;
import org.hamcrest.Matcher;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.test.util.AssertionErrors;
import org.springframework.test.web.client.MockRestServiceServer;
import org.springframework.test.web.client.RequestMatcher;
import org.springframework.util.Assert;
/**
* Static, factory methods for {@link RequestMatcher} classes. Typically used to
* provide input for {@link MockRestServiceServer#expect(RequestMatcher)}.
*
* <p><strong>Eclipse users:</strong> consider adding this class as a Java editor
* favorite. To navigate, open the Preferences and type "favorites".
*
* @author Craig Walls
* @author Rossen Stoyanchev
* @since 3.2
*/
public abstract class MockRestRequestMatchers {
/**
* Private class constructor.
*/
private MockRestRequestMatchers() {
}
/**
* Match to any request.
*/
public static RequestMatcher anything() {
return new RequestMatcher() {
@Override
public void match(ClientHttpRequest request) throws AssertionError {
}
};
}
/**
* Assert the request URI string with the given matcher.
*
* @param matcher String matcher for the expected URI
* @return the request matcher
*/
public static RequestMatcher requestTo(final Matcher<String> matcher) {
Assert.notNull(matcher, "'matcher' must not be null");
return new RequestMatcher() {
@Override
public void match(ClientHttpRequest request) throws IOException, AssertionError {
assertThat("Request URI", request.getURI().toString(), matcher);
}
};
}
/**
* Assert the request URI string.
*
* @param expectedUri the expected URI
* @return the request matcher
*/
public static RequestMatcher requestTo(final String expectedUri) {
Assert.notNull(expectedUri, "'uri' must not be null");
return new RequestMatcher() {
@Override
public void match(ClientHttpRequest request) throws IOException, AssertionError {
assertEquals("Request URI", expectedUri, request.getURI().toString());
}
};
}
/**
* Assert the {@link HttpMethod} of the request.
*
* @param method the HTTP method
* @return the request matcher
*/
public static RequestMatcher method(final HttpMethod method) {
Assert.notNull(method, "'method' must not be null");
return new RequestMatcher() {
@Override
public void match(ClientHttpRequest request) throws AssertionError {
AssertionErrors.assertEquals("Unexpected HttpMethod", method, request.getMethod());
}
};
}
/**
* Expect a request to the given URI.
*
* @param uri the expected URI
* @return the request matcher
*/
public static RequestMatcher requestTo(final URI uri) {
Assert.notNull(uri, "'uri' must not be null");
return new RequestMatcher() {
@Override
public void match(ClientHttpRequest request) throws IOException, AssertionError {
AssertionErrors.assertEquals("Unexpected request", uri, request.getURI());
}
};
}
/**
* Assert request header values with the given Hamcrest matcher.
*/
public static RequestMatcher header(final String name, final Matcher<? super String>... matchers) {
return new RequestMatcher() {
@Override
public void match(ClientHttpRequest request) {
assertHeaderValueCount(name, request.getHeaders(), matchers.length);
for (int i = 0 ; i < matchers.length; i++) {
assertThat("Request header", request.getHeaders().get(name).get(i), matchers[i]);
}
}
};
}
/**
* Assert request header values.
*/
public static RequestMatcher header(final String name, final String... expectedValues) {
return new RequestMatcher() {
@Override
public void match(ClientHttpRequest request) {
assertHeaderValueCount(name, request.getHeaders(), expectedValues.length);
for (int i = 0 ; i < expectedValues.length; i++) {
assertEquals("Request header + [" + name + "]",
expectedValues[i], request.getHeaders().get(name).get(i));
}
}
};
}
private static void assertHeaderValueCount(final String name, HttpHeaders headers, int expectedCount) {
List<String> actualValues = headers.get(name);
AssertionErrors.assertTrue("Expected header <" + name + ">", actualValues != null);
AssertionErrors.assertTrue("Expected header <" + name + "> to have at least <" + expectedCount
+ "> values but found " + actualValues, expectedCount <= actualValues.size());
}
/**
* Access to request body matchers.
*/
public static ContentRequestMatchers content() {
return new ContentRequestMatchers();
}
/**
* Access to request body matchers using a <a
* href="http://goessner.net/articles/JsonPath/">JSONPath</a> expression to
* inspect a specific subset of the body. The JSON path expression can be a
* parameterized string using formatting specifiers as defined in
* {@link String#format(String, Object...)}.
*
* @param expression the JSON path optionally parameterized with arguments
* @param args arguments to parameterize the JSON path expression with
*/
public static JsonPathRequestMatchers jsonPath(String expression, Object ... args) {
return new JsonPathRequestMatchers(expression, args);
}
/**
* Access to request body matchers using a <a
* href="http://goessner.net/articles/JsonPath/">JSONPath</a> expression to
* inspect a specific subset of the body and a Hamcrest match for asserting
* the value found at the JSON path.
*
* @param expression the JSON path expression
* @param matcher a matcher for the value expected at the JSON path
*/
public static <T> RequestMatcher jsonPath(String expression, Matcher<T> matcher) {
return new JsonPathRequestMatchers(expression).value(matcher);
}
/**
* Access to request body matchers using an XPath to inspect a specific
* subset of the body. The XPath expression can be a parameterized string
* using formatting specifiers as defined in
* {@link String#format(String, Object...)}.
*
* @param expression the XPath optionally parameterized with arguments
* @param args arguments to parameterize the XPath expression with
*/
public static XpathRequestMatchers xpath(String expression, Object... args) throws XPathExpressionException {
return new XpathRequestMatchers(expression, null, args);
}
/**
* Access to response body matchers using an XPath to inspect a specific
* subset of the body. The XPath expression can be a parameterized string
* using formatting specifiers as defined in
* {@link String#format(String, Object...)}.
*
* @param expression the XPath optionally parameterized with arguments
* @param namespaces namespaces referenced in the XPath expression
* @param args arguments to parameterize the XPath expression with
*/
public static XpathRequestMatchers xpath(String expression, Map<String, String> namespaces, Object... args)
throws XPathExpressionException {
return new XpathRequestMatchers(expression, namespaces, args);
}
}

View File

@@ -0,0 +1,203 @@
/*
* Copyright 2002-2012 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.match;
import java.io.IOException;
import java.util.Map;
import javax.xml.xpath.XPathExpressionException;
import org.hamcrest.Matcher;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.mock.http.client.MockClientHttpRequest;
import org.springframework.test.util.XpathExpectationsHelper;
import org.springframework.test.web.client.RequestMatcher;
import org.w3c.dom.Node;
/**
* Factory methods for request content {@code RequestMatcher}'s using an XPath
* expression. An instance of this class is typically accessed via
* {@code RequestMatchers.xpath(..)}.
*
* @author Rossen Stoyanchev
* @since 3.2
*/
public class XpathRequestMatchers {
private final XpathExpectationsHelper xpathHelper;
/**
* Class constructor, not for direct instantiation. Use
* {@link MockRestRequestMatchers#xpath(String, Object...)} or
* {@link MockRestRequestMatchers#xpath(String, Map, Object...)}.
*
* @param expression the XPath expression
* @param namespaces XML namespaces referenced in the XPath expression, or {@code null}
* @param args arguments to parameterize the XPath expression with using the
* formatting specifiers defined in {@link String#format(String, Object...)}
*
* @throws XPathExpressionException
*/
protected XpathRequestMatchers(String expression, Map<String, String> namespaces, Object ... args)
throws XPathExpressionException {
this.xpathHelper = new XpathExpectationsHelper(expression, namespaces, args);
}
/**
* Apply the XPath and assert it with the given {@code Matcher<Node>}.
*/
public <T> RequestMatcher node(final Matcher<? super Node> matcher) {
return new AbstractXpathRequestMatcher() {
@Override
protected void matchInternal(MockClientHttpRequest request) throws Exception {
xpathHelper.assertNode(request.getBodyAsString(), matcher);
}
};
}
/**
* Assert that content exists at the given XPath.
*/
public <T> RequestMatcher exists() {
return new AbstractXpathRequestMatcher() {
@Override
protected void matchInternal(MockClientHttpRequest request) throws Exception {
xpathHelper.exists(request.getBodyAsString());
}
};
}
/**
* Assert that content does not exist at the given XPath.
*/
public <T> RequestMatcher doesNotExist() {
return new AbstractXpathRequestMatcher() {
@Override
protected void matchInternal(MockClientHttpRequest request) throws Exception {
xpathHelper.doesNotExist(request.getBodyAsString());
}
};
}
/**
* Apply the XPath and assert the number of nodes found with the given
* {@code Matcher<Integer>}.
*/
public <T> RequestMatcher nodeCount(final Matcher<Integer> matcher) {
return new AbstractXpathRequestMatcher() {
@Override
protected void matchInternal(MockClientHttpRequest request) throws Exception {
xpathHelper.assertNodeCount(request.getBodyAsString(), matcher);
}
};
}
/**
* Apply the XPath and assert the number of nodes found.
*/
public <T> RequestMatcher nodeCount(final int expectedCount) {
return new AbstractXpathRequestMatcher() {
@Override
protected void matchInternal(MockClientHttpRequest request) throws Exception {
xpathHelper.assertNodeCount(request.getBodyAsString(), expectedCount);
}
};
}
/**
* Apply the XPath and assert the String content found with the given matcher.
*/
public <T> RequestMatcher string(final Matcher<? super String> matcher) {
return new AbstractXpathRequestMatcher() {
@Override
protected void matchInternal(MockClientHttpRequest request) throws Exception {
xpathHelper.assertString(request.getBodyAsString(), matcher);
}
};
}
/**
* Apply the XPath and assert the String content found.
*/
public RequestMatcher string(final String value) {
return new AbstractXpathRequestMatcher() {
@Override
protected void matchInternal(MockClientHttpRequest request) throws Exception {
xpathHelper.assertString(request.getBodyAsString(), value);
}
};
}
/**
* Apply the XPath and assert the number found with the given matcher.
*/
public <T> RequestMatcher number(final Matcher<? super Double> matcher) {
return new AbstractXpathRequestMatcher() {
@Override
protected void matchInternal(MockClientHttpRequest request) throws Exception {
xpathHelper.assertNumber(request.getBodyAsString(), matcher);
}
};
}
/**
* Apply the XPath and assert the number of nodes found.
*/
public RequestMatcher number(final Double value) {
return new AbstractXpathRequestMatcher() {
@Override
protected void matchInternal(MockClientHttpRequest request) throws Exception {
xpathHelper.assertNumber(request.getBodyAsString(), value);
}
};
}
/**
* Apply the XPath and assert the boolean value found.
*/
public <T> RequestMatcher booleanValue(final Boolean value) {
return new AbstractXpathRequestMatcher() {
@Override
protected void matchInternal(MockClientHttpRequest request) throws Exception {
xpathHelper.assertBoolean(request.getBodyAsString(), value);
}
};
}
/**
* Abstract base class for XPath {@link RequestMatcher}'s.
*/
private abstract static class AbstractXpathRequestMatcher implements RequestMatcher {
@Override
public final void match(ClientHttpRequest request) throws IOException, AssertionError {
try {
MockClientHttpRequest mockRequest = (MockClientHttpRequest) request;
matchInternal(mockRequest);
}
catch (Exception e) {
throw new AssertionError("Failed to parse XML request content: " + e.getMessage());
}
}
protected abstract void matchInternal(MockClientHttpRequest request) throws Exception;
}
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright 2002-2012 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.
*/
/**
* Contains built-in {@link org.springframework.test.web.client.RequestMatcher}
* implementations. Use
* {@link org.springframework.test.web.client.match.MockRestRequestMatchers}
* to gain access to instances of those implementations.
*/
package org.springframework.test.web.client.match;

View File

@@ -0,0 +1,21 @@
/*
* Copyright 2002-2012 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.
*/
/**
* Contains client-side REST testing support.
* @see org.springframework.test.web.client.MockRestServiceServer
*/
package org.springframework.test.web.client;

View File

@@ -0,0 +1,133 @@
/*
* Copyright 2002-2012 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.response;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.mock.http.client.MockClientHttpResponse;
import org.springframework.test.web.client.ResponseCreator;
import org.springframework.util.Assert;
/**
* A {@code ResponseCreator} with builder-style methods for adding response details.
*
* @author Rossen Stoyanchev
* @since 3.2
*/
public class DefaultResponseCreator implements ResponseCreator {
private byte[] content;
private Resource contentResource;
private final HttpHeaders headers = new HttpHeaders();
private HttpStatus statusCode;
/**
* Protected constructor.
* Use static factory methods in {@link MockRestResponseCreators}.
*/
protected DefaultResponseCreator(HttpStatus statusCode) {
Assert.notNull(statusCode);
this.statusCode = statusCode;
}
@Override
public ClientHttpResponse createResponse(ClientHttpRequest request) throws IOException {
MockClientHttpResponse response;
if (this.contentResource != null ){
InputStream stream = this.contentResource.getInputStream();
response = new MockClientHttpResponse(stream, this.statusCode);
}
else {
response = new MockClientHttpResponse(this.content, this.statusCode);
}
response.getHeaders().putAll(this.headers);
return response;
}
/**
* Set the body as a UTF-8 String.
*/
public DefaultResponseCreator body(String content) {
try {
this.content = content.getBytes("UTF-8");
}
catch (UnsupportedEncodingException e) {
// should not happen, UTF-8 is always supported
throw new IllegalStateException(e);
}
return this;
}
/**
* Set the body as a byte array.
*/
public DefaultResponseCreator body(byte[] content) {
this.content = content;
return this;
}
/**
* Set the body as a {@link Resource}.
*/
public DefaultResponseCreator body(Resource resource) {
this.contentResource = resource;
return this;
}
/**
* Set the {@code Content-Type} header.
*/
public DefaultResponseCreator contentType(MediaType mediaType) {
if (mediaType != null) {
this.headers.setContentType(mediaType);
}
return this;
}
/**
* Set the {@code Location} header.
*/
public DefaultResponseCreator location(URI location) {
this.headers.setLocation(location);
return this;
}
/**
* Copy all given headers.
*/
public DefaultResponseCreator headers(HttpHeaders headers) {
for (String headerName : headers.keySet()) {
for (String headerValue : headers.get(headerName)) {
this.headers.add(headerName, headerValue);
}
}
return this;
}
}

View File

@@ -0,0 +1,118 @@
/*
* Copyright 2002-2012 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.response;
import java.net.URI;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.test.web.client.ResponseCreator;
/**
* Static factory methods for obtaining a {@link ResponseCreator} instance.
*
* <p><strong>Eclipse users:</strong> consider adding this class as a Java editor
* favorite. To navigate, open the Preferences and type "favorites".
*
* @author Rossen Stoyanchev
* @since 3.2
*/
public abstract class MockRestResponseCreators {
private MockRestResponseCreators() {
}
/**
* {@code ResponseCreator} for a 200 response (OK).
*/
public static DefaultResponseCreator withSuccess() {
return new DefaultResponseCreator(HttpStatus.OK);
}
/**
* {@code ResponseCreator} for a 200 response (OK) with String body.
* @param body the response body, a "UTF-8" string
* @param mediaType the type of the content, may be {@code null}
*/
public static DefaultResponseCreator withSuccess(String body, MediaType mediaType) {
return new DefaultResponseCreator(HttpStatus.OK).body(body).contentType(mediaType);
}
/**
* {@code ResponseCreator} for a 200 response (OK) with byte[] body.
* @param body the response body
* @param contentType the type of the content, may be {@code null}
*/
public static DefaultResponseCreator withSuccess(byte[] body, MediaType contentType) {
return new DefaultResponseCreator(HttpStatus.OK).body(body).contentType(contentType);
}
/**
* {@code ResponseCreator} for a 200 response (OK) content with {@link Resource}-based body.
* @param body the response body
* @param contentType the type of the content, may be {@code null}
*/
public static DefaultResponseCreator withSuccess(Resource body, MediaType contentType) {
return new DefaultResponseCreator(HttpStatus.OK).body(body).contentType(contentType);
}
/**
* {@code ResponseCreator} for a 201 response (CREATED) with a 'Location' header.
* @param location the value for the {@code Location} header
*/
public static DefaultResponseCreator withCreatedEntity(URI location) {
return new DefaultResponseCreator(HttpStatus.CREATED).location(location);
}
/**
* {@code ResponseCreator} for a 204 response (NO_CONTENT).
*/
public static DefaultResponseCreator withNoContent() {
return new DefaultResponseCreator(HttpStatus.NO_CONTENT);
}
/**
* {@code ResponseCreator} for a 400 response (BAD_REQUEST).
*/
public static DefaultResponseCreator withBadRequest() {
return new DefaultResponseCreator(HttpStatus.BAD_REQUEST);
}
/**
* {@code ResponseCreator} for a 401 response (UNAUTHORIZED).
*/
public static DefaultResponseCreator withUnauthorizedRequest() {
return new DefaultResponseCreator(HttpStatus.UNAUTHORIZED);
}
/**
* {@code ResponseCreator} for a 500 response (SERVER_ERROR).
*/
public static DefaultResponseCreator withServerError() {
return new DefaultResponseCreator(HttpStatus.INTERNAL_SERVER_ERROR);
}
/**
* {@code ResponseCreator} with a specific HTTP status.
* @param status the response status
*/
public static DefaultResponseCreator withStatus(HttpStatus status) {
return new DefaultResponseCreator(status);
}
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright 2002-2012 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.
*/
/**
* Contains built-in {@link org.springframework.test.web.client.ResponseCreator}
* implementations. Use
* {@link org.springframework.test.web.client.response.MockRestResponseCreators}
* to gain access to instances of those implementations.
*/
package org.springframework.test.web.client.response;

View File

@@ -0,0 +1,156 @@
/*
* Copyright 2002-2012 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.servlet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import javax.servlet.http.HttpServletRequest;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.web.servlet.FlashMap;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.support.RequestContextUtils;
/**
* A simple implementation of {@link MvcResult} with setters.
*
* @author Rossen Stoyanchev
* @author Rob Winch
* @since 3.2
*/
class DefaultMvcResult implements MvcResult {
private final MockHttpServletRequest mockRequest;
private final MockHttpServletResponse mockResponse;
private Object handler;
private HandlerInterceptor[] interceptors;
private ModelAndView modelAndView;
private Exception resolvedException;
private Object asyncResult;
private CountDownLatch asyncResultLatch;
/**
* Create a new instance with the given request and response.
*/
public DefaultMvcResult(MockHttpServletRequest request, MockHttpServletResponse response) {
this.mockRequest = request;
this.mockResponse = response;
}
@Override
public MockHttpServletResponse getResponse() {
return mockResponse;
}
@Override
public MockHttpServletRequest getRequest() {
return mockRequest;
}
@Override
public Object getHandler() {
return this.handler;
}
public void setHandler(Object handler) {
this.handler = handler;
}
@Override
public HandlerInterceptor[] getInterceptors() {
return this.interceptors;
}
public void setInterceptors(HandlerInterceptor[] interceptors) {
this.interceptors = interceptors;
}
@Override
public Exception getResolvedException() {
return this.resolvedException;
}
public void setResolvedException(Exception resolvedException) {
this.resolvedException = resolvedException;
}
@Override
public ModelAndView getModelAndView() {
return this.modelAndView;
}
public void setModelAndView(ModelAndView mav) {
this.modelAndView = mav;
}
@Override
public FlashMap getFlashMap() {
return RequestContextUtils.getOutputFlashMap(mockRequest);
}
public void setAsyncResult(Object asyncResult) {
this.asyncResult = asyncResult;
}
@Override
public Object getAsyncResult() {
return getAsyncResult(-1);
}
@Override
public Object getAsyncResult(long timeout) {
// MockHttpServletRequest type doesn't have async methods
HttpServletRequest request = this.mockRequest;
if ((timeout != 0) && request.isAsyncStarted()) {
if (timeout == -1) {
timeout = request.getAsyncContext().getTimeout();
}
if (!awaitAsyncResult(timeout)) {
throw new IllegalStateException(
"Gave up waiting on async result from handler [" + this.handler + "] to complete");
}
}
return this.asyncResult;
}
private boolean awaitAsyncResult(long timeout) {
if (this.asyncResultLatch != null) {
try {
return this.asyncResultLatch.await(timeout, TimeUnit.MILLISECONDS);
}
catch (InterruptedException e) {
return false;
}
}
return true;
}
public void setAsyncResultLatch(CountDownLatch asyncResultLatch) {
this.asyncResultLatch = asyncResultLatch;
}
}

View File

@@ -0,0 +1,177 @@
/*
* Copyright 2002-2013 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.servlet;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.Filter;
import javax.servlet.ServletContext;
import org.springframework.beans.Mergeable;
import org.springframework.mock.web.MockFilterChain;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.util.Assert;
/**
* <strong>Main entry point for server-side Spring MVC test support.</strong>
*
* <p>Below is an example:
*
* <pre class="code">
* static imports:
* MockMvcBuilders.*, MockMvcRequestBuilders.*, MockMvcResultMatchers.*
*
* WebApplicationContext wac = ...;
*
* MockMvc mockMvc = webAppContextSetup(wac).build();
*
* mockMvc.perform(get("/form"))
* .andExpect(status().isOk())
* .andExpect(content().mimeType("text/html"))
* .andExpect(forwardedUrl("/WEB-INF/layouts/main.jsp"));
* </pre>
*
* @author Rossen Stoyanchev
* @author Rob Winch
* @since 3.2
*/
public final class MockMvc {
static String MVC_RESULT_ATTRIBUTE = MockMvc.class.getName().concat(".MVC_RESULT_ATTRIBUTE");
private final TestDispatcherServlet servlet;
private final Filter[] filters;
private final ServletContext servletContext;
private RequestBuilder defaultRequestBuilder;
private List<ResultMatcher> defaultResultMatchers = new ArrayList<ResultMatcher>();
private List<ResultHandler> defaultResultHandlers = new ArrayList<ResultHandler>();
/**
* Private constructor, not for direct instantiation.
* @see org.springframework.test.web.servlet.setup.MockMvcBuilders
*/
MockMvc(TestDispatcherServlet servlet, Filter[] filters, ServletContext servletContext) {
Assert.notNull(servlet, "DispatcherServlet is required");
Assert.notNull(filters, "filters cannot be null");
Assert.noNullElements(filters, "filters cannot contain null values");
Assert.notNull(servletContext, "A ServletContext is required");
this.servlet = servlet;
this.filters = filters;
this.servletContext = servletContext;
}
/**
* A default request builder merged into every performed request.
* @see org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder#defaultRequest(RequestBuilder)
*/
void setDefaultRequest(RequestBuilder requestBuilder) {
this.defaultRequestBuilder = requestBuilder;
}
/**
* Expectations to assert after every performed request.
* @see org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder#alwaysExpect(ResultMatcher)
*/
void setGlobalResultMatchers(List<ResultMatcher> resultMatchers) {
Assert.notNull(resultMatchers, "resultMatchers is required");
this.defaultResultMatchers = resultMatchers;
}
/**
* General actions to apply after every performed request.
* @see org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder#alwaysDo(ResultHandler)
*/
void setGlobalResultHandlers(List<ResultHandler> resultHandlers) {
Assert.notNull(resultHandlers, "resultHandlers is required");
this.defaultResultHandlers = resultHandlers;
}
/**
* Perform a request and return a type that allows chaining further
* actions, such as asserting expectations, on the result.
*
* @param requestBuilder used to prepare the request to execute;
* see static factory methods in
* {@link org.springframework.test.web.servlet.request.MockMvcRequestBuilders}
*
* @return an instance of {@link ResultActions}; never {@code null}
*
* @see org.springframework.test.web.servlet.request.MockMvcRequestBuilders
* @see org.springframework.test.web.servlet.result.MockMvcResultMatchers
*/
public ResultActions perform(RequestBuilder requestBuilder) throws Exception {
if (this.defaultRequestBuilder != null) {
if (requestBuilder instanceof Mergeable) {
requestBuilder = (RequestBuilder) ((Mergeable) requestBuilder).merge(this.defaultRequestBuilder);
}
}
MockHttpServletRequest request = requestBuilder.buildRequest(this.servletContext);
MockHttpServletResponse response = new MockHttpServletResponse();
final MvcResult mvcResult = new DefaultMvcResult(request, response);
request.setAttribute(MVC_RESULT_ATTRIBUTE, mvcResult);
MockFilterChain filterChain = new MockFilterChain(this.servlet, this.filters);
filterChain.doFilter(request, response);
applyDefaultResultActions(mvcResult);
return new ResultActions() {
@Override
public ResultActions andExpect(ResultMatcher matcher) throws Exception {
matcher.match(mvcResult);
return this;
}
@Override
public ResultActions andDo(ResultHandler printer) throws Exception {
printer.handle(mvcResult);
return this;
}
@Override
public MvcResult andReturn() {
return mvcResult;
}
};
}
private void applyDefaultResultActions(MvcResult mvcResult) throws Exception {
for (ResultMatcher matcher : this.defaultResultMatchers) {
matcher.match(mvcResult);
}
for (ResultHandler handler : this.defaultResultHandlers) {
handler.handle(mvcResult);
}
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright 2002-2012 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.servlet;
/**
* Builds a {@link MockMvc}.
*
* <p>See static, factory methods in
* {@code org.springframework.test.web.server.setup.MockMvcBuilders}.
*
* @author Rossen Stoyanchev
* @since 3.2
*/
public interface MockMvcBuilder {
/**
* Build a {@link MockMvc} instance.
*/
MockMvc build();
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright 2002-2013 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.servlet;
import java.util.List;
import javax.servlet.Filter;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import org.springframework.core.NestedRuntimeException;
import org.springframework.mock.web.MockServletConfig;
import org.springframework.web.context.WebApplicationContext;
/**
* Base class for MockMvc builder implementations, providing the capability to
* create a {@link MockMvc} instance.
*
* <p>{@link org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder},
* which derives from this class, provides a concrete {@code build} method,
* and delegates to abstract methods to obtain a {@link WebApplicationContext}.
*
* @author Rossen Stoyanchev
* @author Rob Winch
* @since 3.2
*/
public abstract class MockMvcBuilderSupport {
protected final MockMvc createMockMvc(Filter[] filters, MockServletConfig servletConfig,
WebApplicationContext webAppContext, RequestBuilder defaultRequestBuilder,
List<ResultMatcher> globalResultMatchers, List<ResultHandler> globalResultHandlers, Boolean dispatchOptions) {
ServletContext servletContext = webAppContext.getServletContext();
TestDispatcherServlet dispatcherServlet = new TestDispatcherServlet(webAppContext);
dispatcherServlet.setDispatchOptionsRequest(dispatchOptions);
try {
dispatcherServlet.init(servletConfig);
}
catch (ServletException ex) {
// should never happen..
throw new MockMvcBuildException("Failed to initialize TestDispatcherServlet", ex);
}
MockMvc mockMvc = new MockMvc(dispatcherServlet, filters, servletContext);
mockMvc.setDefaultRequest(defaultRequestBuilder);
mockMvc.setGlobalResultMatchers(globalResultMatchers);
mockMvc.setGlobalResultHandlers(globalResultHandlers);
return mockMvc;
}
@SuppressWarnings("serial")
private static class MockMvcBuildException extends NestedRuntimeException {
public MockMvcBuildException(String msg, Throwable cause) {
super(msg, cause);
}
}
}

View File

@@ -0,0 +1,99 @@
/*
* Copyright 2002-2012 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.servlet;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.web.servlet.FlashMap;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
/**
* Provides access to the result of an executed request.
*
* @author Rossen Stoyanchev
* @since 3.2
*/
public interface MvcResult {
/**
* Return the performed request.
* @return the request, never {@code null}
*/
MockHttpServletRequest getRequest();
/**
* Return the resulting response.
* @return the response, never {@code null}
*/
MockHttpServletResponse getResponse();
/**
* Return the executed handler.
* @return the handler, possibly {@code null} if none were executed
*/
Object getHandler();
/**
* Return interceptors around the handler.
* @return interceptors, or {@code null} if none were selected
*/
HandlerInterceptor[] getInterceptors();
/**
* Return the {@code ModelAndView} prepared by the handler.
* @return a {@code ModelAndView}, or {@code null}
*/
ModelAndView getModelAndView();
/**
* Return any exception raised by a handler and successfully resolved
* through a {@link HandlerExceptionResolver}.
*
* @return an exception, possibly {@code null}
*/
Exception getResolvedException();
/**
* Return the "output" flash attributes saved during request processing.
* @return the {@code FlashMap}, possibly empty
*/
FlashMap getFlashMap();
/**
* Get the result of asynchronous execution or {@code null} if concurrent
* handling did not start. This method will hold and await the completion
* of concurrent handling.
*
* @throws IllegalStateException if concurrent handling does not complete
* within the allocated async timeout value.
*/
Object getAsyncResult();
/**
* Get the result of asynchronous execution or {@code null} if concurrent
* handling did not start. This method will wait for up to the given timeout
* for the completion of concurrent handling.
*
* @param timeout how long to wait for the async result to be set in
* milliseconds; if -1, the wait will be as long as the async timeout set
* on the Servlet request
*/
Object getAsyncResult(long timeout);
}

View File

@@ -0,0 +1,27 @@
package org.springframework.test.web.servlet;
import javax.servlet.ServletContext;
import org.springframework.mock.web.MockHttpServletRequest;
/**
* Builds a {@link MockHttpServletRequest}.
*
* <p>See static, factory methods in
* {@code org.springframework.test.web.server.request.MockMvcRequestBuilders}.
*
* @author Arjen Poutsma
* @author Rossen Stoyanchev
* @since 3.2
*/
public interface RequestBuilder {
/**
* Build the request.
*
* @param servletContext the {@link ServletContext} to use to create the request
* @return the request
*/
MockHttpServletRequest buildRequest(ServletContext servletContext);
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright 2002-2012 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.servlet;
/**
* Allows applying actions, such as expectations, on the result of an executed
* request.
*
* <p>See static factory methods in
* {@code org.springframework.test.web.server.result.MockMvcResultMatchers}
* {@code org.springframework.test.web.server.result.MockMvcResultHandlers}
*
* @author Rossen Stoyanchev
* @since 3.2
*/
public interface ResultActions {
/**
* Provide an expectation. For example:
* <pre class="code">
* static imports: MockMvcRequestBuilders.*, MockMvcResultMatchers.*
*
* mockMvc.perform(get("/person/1"))
* .andExpect(status.isOk())
* .andExpect(content().mimeType(MediaType.APPLICATION_JSON))
* .andExpect(jsonPath("$.person.name").equalTo("Jason"));
*
* mockMvc.perform(post("/form"))
* .andExpect(status.isOk())
* .andExpect(redirectedUrl("/person/1"))
* .andExpect(model().size(1))
* .andExpect(model().attributeExists("person"))
* .andExpect(flash().attributeCount(1))
* .andExpect(flash().attribute("message", "success!"));
* </pre>
*/
ResultActions andExpect(ResultMatcher matcher) throws Exception;
/**
* Provide a general action. For example:
* <pre class="code">
* static imports: MockMvcRequestBuilders.*, MockMvcResultMatchers.*
*
* mockMvc.perform(get("/form")).andDo(print());
* </pre>
*/
ResultActions andDo(ResultHandler handler) throws Exception;
/**
* Return the result of the executed request for direct access to the results.
*
* @return the result of the request
*/
MvcResult andReturn();
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2002-2012 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.servlet;
/**
* Executes a generic action (e.g. printing debug information) on the result of
* an executed request.
*
* <p>See static factory methods in
* {@code org.springframework.test.web.server.result.MockMvcResultHandlers}.
*
* <p>Example:
*
* <pre class="code">
* static imports: MockMvcRequestBuilders.*, MockMvcResultHandlers.*
*
* mockMvc.perform(get("/form")).andDo(print());
* </pre>
*
* @author Rossen Stoyanchev
* @since 3.2
*/
public interface ResultHandler {
/**
* Apply the action on the given result.
*
* @param result the result of the executed request
* @throws Exception if a failure occurs
*/
void handle(MvcResult result) throws Exception;
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2002-2012 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.servlet;
/**
* Matches the result of an executed request against some expectation.
*
* <p>See static factory methods in
* {@code org.springframework.test.web.server.result.MockMvcResultMatchers}.
*
* <p>Example:
*
* <pre class="code">
* static imports: MockMvcRequestBuilders.*, MockMvcResultMatchers.*
*
* mockMvc.perform(get("/form"))
* .andExpect(status.isOk())
* .andExpect(content().mimeType(MediaType.APPLICATION_JSON));
* </pre>
*
* @author Rossen Stoyanchev
* @since 3.2
*/
public interface ResultMatcher {
/**
* Assert the result of an executed request.
*
* @param result the result of the executed request
* @throws Exception if a failure occurs
*/
void match(MvcResult result) throws Exception;
}

View File

@@ -0,0 +1,131 @@
/*
* Copyright 2002-2012 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.servlet;
import java.io.IOException;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.async.CallableProcessingInterceptorAdapter;
import org.springframework.web.context.request.async.DeferredResult;
import org.springframework.web.context.request.async.DeferredResultProcessingInterceptorAdapter;
import org.springframework.web.context.request.async.WebAsyncManager;
import org.springframework.web.context.request.async.WebAsyncUtils;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.ModelAndView;
/**
* A sub-class of {@code DispatcherServlet} that saves the result in an
* {@link MvcResult}. The {@code MvcResult} instance is expected to be available
* as the request attribute {@link MockMvc#MVC_RESULT_ATTRIBUTE}.
*
* @author Rossen Stoyanchev
* @author Rob Winch
* @since 3.2
*/
@SuppressWarnings("serial")
final class TestDispatcherServlet extends DispatcherServlet {
private static final String KEY = TestDispatcherServlet.class.getName() + ".interceptor";
/**
* Create a new instance with the given web application context.
*/
public TestDispatcherServlet(WebApplicationContext webApplicationContext) {
super(webApplicationContext);
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
CountDownLatch latch = registerAsyncInterceptors(request);
getMvcResult(request).setAsyncResultLatch(latch);
super.service(request, response);
}
private CountDownLatch registerAsyncInterceptors(final HttpServletRequest servletRequest) {
final CountDownLatch asyncResultLatch = new CountDownLatch(1);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(servletRequest);
asyncManager.registerCallableInterceptor(KEY, new CallableProcessingInterceptorAdapter() {
@Override
public <T> void postProcess(NativeWebRequest request, Callable<T> task, Object value) throws Exception {
getMvcResult(servletRequest).setAsyncResult(value);
asyncResultLatch.countDown();
}
});
asyncManager.registerDeferredResultInterceptor(KEY, new DeferredResultProcessingInterceptorAdapter() {
@Override
public <T> void postProcess(NativeWebRequest request, DeferredResult<T> result, Object value) throws Exception {
getMvcResult(servletRequest).setAsyncResult(value);
asyncResultLatch.countDown();
}
});
return asyncResultLatch;
}
protected DefaultMvcResult getMvcResult(ServletRequest request) {
return (DefaultMvcResult) request.getAttribute(MockMvc.MVC_RESULT_ATTRIBUTE);
}
@Override
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
HandlerExecutionChain chain = super.getHandler(request);
if (chain != null) {
DefaultMvcResult mvcResult = getMvcResult(request);
mvcResult.setHandler(chain.getHandler());
mvcResult.setInterceptors(chain.getInterceptors());
}
return chain;
}
@Override
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response)
throws Exception {
DefaultMvcResult mvcResult = getMvcResult(request);
mvcResult.setModelAndView(mv);
super.render(mv, request, response);
}
@Override
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
ModelAndView mav = super.processHandlerException(request, response, handler, ex);
// We got this far, exception was processed..
DefaultMvcResult mvcResult = getMvcResult(request);
mvcResult.setResolvedException(ex);
mvcResult.setModelAndView(mav);
return mav;
}
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright 2002-2012 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.
*/
/**
* Contains server-side support for testing Spring MVC applications.
* @see org.springframework.test.web.servlet.MockMvc
*/
package org.springframework.test.web.servlet;

View File

@@ -0,0 +1,720 @@
/*
* Copyright 2002-2013 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.servlet.request;
import java.io.UnsupportedEncodingException;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import javax.servlet.ServletContext;
import javax.servlet.ServletRequest;
import javax.servlet.http.Cookie;
import org.springframework.beans.Mergeable;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.RequestBuilder;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.util.Assert;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.ValueConstants;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.FlashMap;
import org.springframework.web.servlet.FlashMapManager;
import org.springframework.web.servlet.support.SessionFlashMapManager;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import org.springframework.web.util.UriUtils;
/**
* Default builder for {@link MockHttpServletRequest} required as input to
* perform request in {@link MockMvc}.
*
* <p>Application tests will typically access this builder through the static
* factory methods in {@link MockMvcBuilders}.
*
* @author Rossen Stoyanchev
* @author Arjen Poutsma
* @since 3.2
*/
public class MockHttpServletRequestBuilder implements RequestBuilder, Mergeable {
private final UriComponents uriComponents;
private final HttpMethod method;
private final MultiValueMap<String, Object> headers = new LinkedMultiValueMap<String, Object>();
private String contentType;
private byte[] content;
private final MultiValueMap<String, String> parameters = new LinkedMultiValueMap<String, String>();
private final List<Cookie> cookies = new ArrayList<Cookie>();
private Locale locale;
private String characterEncoding;
private Principal principal;
private Boolean secure;
private final Map<String, Object> attributes = new LinkedHashMap<String, Object>();
private MockHttpSession session;
private final Map<String, Object> sessionAttributes = new LinkedHashMap<String, Object>();
private final Map<String, Object> flashAttributes = new LinkedHashMap<String, Object>();
private String contextPath = "";
private String servletPath = "";
private String pathInfo = ValueConstants.DEFAULT_NONE;
private final List<RequestPostProcessor> postProcessors =
new ArrayList<RequestPostProcessor>();
/**
* Package private constructor. To get an instance, use static factory
* methods in {@link MockMvcRequestBuilders}.
*
* <p>Although this class cannot be extended, additional ways to initialize
* the {@code MockHttpServletRequest} can be plugged in via
* {@link #with(RequestPostProcessor)}.
*
* @param urlTemplate a URL template; the resulting URL will be encoded
* @param urlVariables zero or more URL variables
*/
MockHttpServletRequestBuilder(HttpMethod httpMethod, String urlTemplate, Object... urlVariables) {
Assert.notNull(urlTemplate, "uriTemplate is required");
Assert.notNull(httpMethod, "httpMethod is required");
this.uriComponents = UriComponentsBuilder.fromUriString(urlTemplate).buildAndExpand(urlVariables).encode();
this.method = httpMethod;
}
/**
* Add a request parameter to the {@link MockHttpServletRequest}.
* If called more than once, the new values are added.
*
* @param name the parameter name
* @param values one or more values
*/
public MockHttpServletRequestBuilder param(String name, String... values) {
addToMultiValueMap(this.parameters, name, values);
return this;
}
/**
* Add a header to the request. Values are always added.
*
* @param name the header name
* @param values one or more header values
*/
public MockHttpServletRequestBuilder header(String name, Object... values) {
addToMultiValueMap(this.headers, name, values);
return this;
}
/**
* Add all headers to the request. Values are always added.
*
* @param httpHeaders the headers and values to add
*/
public MockHttpServletRequestBuilder headers(HttpHeaders httpHeaders) {
for (String name : httpHeaders.keySet()) {
Object[] values = ObjectUtils.toObjectArray(httpHeaders.get(name).toArray());
addToMultiValueMap(this.headers, name, values);
}
return this;
}
/**
* Set the 'Content-Type' header of the request.
*
* @param mediaType the content type
*/
public MockHttpServletRequestBuilder contentType(MediaType mediaType) {
Assert.notNull(mediaType, "'contentType' must not be null");
this.contentType = mediaType.toString();
this.headers.set("Content-Type", this.contentType);
return this;
}
/**
* Set the 'Accept' header to the given media type(s).
*
* @param mediaTypes one or more media types
*/
public MockHttpServletRequestBuilder accept(MediaType... mediaTypes) {
Assert.notEmpty(mediaTypes, "No 'Accept' media types");
this.headers.set("Accept", MediaType.toString(Arrays.asList(mediaTypes)));
return this;
}
/**
* Set the 'Accept' header to the given media type(s).
*
* @param mediaTypes one or more media types
*/
public MockHttpServletRequestBuilder accept(String... mediaTypes) {
Assert.notEmpty(mediaTypes, "No 'Accept' media types");
List<MediaType> result = new ArrayList<MediaType>(mediaTypes.length);
for (String mediaType : mediaTypes) {
result.add(MediaType.parseMediaType(mediaType));
}
this.headers.set("Accept", MediaType.toString(result));
return this;
}
/**
* Set the request body.
*
* @param content the body content
*/
public MockHttpServletRequestBuilder content(byte[] content) {
this.content = content;
return this;
}
/**
* Set the request body as a UTF-8 String.
*
* @param content the body content
*/
public MockHttpServletRequestBuilder content(String content) {
try {
this.content = content.getBytes("UTF-8");
}
catch (UnsupportedEncodingException e) {
// should never happen
}
return this;
}
/**
* Add the given cookies to the request. Cookies are always added.
*
* @param cookies the cookies to add
*/
public MockHttpServletRequestBuilder cookie(Cookie... cookies) {
Assert.notNull(cookies, "'cookies' must not be null");
Assert.notEmpty(cookies, "'cookies' must not be empty");
this.cookies.addAll(Arrays.asList(cookies));
return this;
}
/**
* Set the locale of the request.
*
* @param locale the locale
*/
public MockHttpServletRequestBuilder locale(Locale locale) {
this.locale = locale;
return this;
}
/**
* Set the character encoding of the request.
*
* @param encoding the character encoding
*/
public MockHttpServletRequestBuilder characterEncoding(String encoding) {
this.characterEncoding = encoding;
return this;
}
/**
* Set a request attribute.
*
* @param name the attribute name
* @param value the attribute value
*/
public MockHttpServletRequestBuilder requestAttr(String name, Object value) {
addAttributeToMap(this.attributes, name, value);
return this;
}
/**
* Set a session attribute.
*
* @param name the session attribute name
* @param value the session attribute value
*/
public MockHttpServletRequestBuilder sessionAttr(String name, Object value) {
addAttributeToMap(this.sessionAttributes, name, value);
return this;
}
/**
* Set session attributes.
*
* @param sessionAttributes the session attributes
*/
public MockHttpServletRequestBuilder sessionAttrs(Map<String, Object> sessionAttributes) {
Assert.notEmpty(sessionAttributes, "'sessionAttrs' must not be empty");
for (String name : sessionAttributes.keySet()) {
sessionAttr(name, sessionAttributes.get(name));
}
return this;
}
/**
* Set an "input" flash attribute.
*
* @param name the flash attribute name
* @param value the flash attribute value
*/
public MockHttpServletRequestBuilder flashAttr(String name, Object value) {
addAttributeToMap(this.flashAttributes, name, value);
return this;
}
/**
* Set flash attributes.
*
* @param flashAttributes the flash attributes
*/
public MockHttpServletRequestBuilder flashAttrs(Map<String, Object> flashAttributes) {
Assert.notEmpty(flashAttributes, "'flashAttrs' must not be empty");
for (String name : flashAttributes.keySet()) {
flashAttr(name, flashAttributes.get(name));
}
return this;
}
/**
* Set the HTTP session to use, possibly re-used across requests.
*
* <p>Individual attributes provided via {@link #sessionAttr(String, Object)}
* override the content of the session provided here.
*
* @param session the HTTP session
*/
public MockHttpServletRequestBuilder session(MockHttpSession session) {
Assert.notNull(session, "'session' must not be null");
this.session = session;
return this;
}
/**
* Set the principal of the request.
*
* @param principal the principal
*/
public MockHttpServletRequestBuilder principal(Principal principal) {
Assert.notNull(principal, "'principal' must not be null");
this.principal = principal;
return this;
}
/**
* Specify the portion of the requestURI that represents the context path.
* The context path, if specified, must match to the start of the request
* URI.
*
* <p>In most cases, tests can be written by omitting the context path from
* the requestURI. This is because most applications don't actually depend
* on the name under which they're deployed. If specified here, the context
* path must start with a "/" and must not end with a "/".
*
* @see <a
* href="http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getContextPath%28%29">HttpServletRequest.getContextPath()</a>
*/
public MockHttpServletRequestBuilder contextPath(String contextPath) {
if (StringUtils.hasText(contextPath)) {
Assert.isTrue(contextPath.startsWith("/"), "Context path must start with a '/'");
Assert.isTrue(!contextPath.endsWith("/"), "Context path must not end with a '/'");
}
this.contextPath = (contextPath != null) ? contextPath : "";
return this;
}
/**
* Specify the portion of the requestURI that represents the path to which
* the Servlet is mapped. This is typically a portion of the requestURI
* after the context path.
*
* <p>In most cases, tests can be written by omitting the servlet path from
* the requestURI. This is because most applications don't actually depend
* on the prefix to which a servlet is mapped. For example if a Servlet is
* mapped to {@code "/main/*"}, tests can be written with the requestURI
* {@code "/accounts/1"} as opposed to {@code "/main/accounts/1"}.
* If specified here, the servletPath must start with a "/" and must not
* end with a "/".
*
* @see <a
* href="http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getServletPath%28%29">HttpServletRequest.getServletPath()</a>
*/
public MockHttpServletRequestBuilder servletPath(String servletPath) {
if (StringUtils.hasText(servletPath)) {
Assert.isTrue(servletPath.startsWith("/"), "Servlet path must start with a '/'");
Assert.isTrue(!servletPath.endsWith("/"), "Servlet path must not end with a '/'");
}
this.servletPath = (servletPath != null) ? servletPath : "";
return this;
}
/**
* Specify the portion of the requestURI that represents the pathInfo.
*
* <p>If left unspecified (recommended), the pathInfo will be automatically
* derived by removing the contextPath and the servletPath from the
* requestURI and using any remaining part. If specified here, the pathInfo
* must start with a "/".
*
* <p>If specified, the pathInfo will be used as is.
*
* @see <a
* href="http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getPathInfo%28%29">HttpServletRequest.getServletPath()</a>
*/
public MockHttpServletRequestBuilder pathInfo(String pathInfo) {
if (StringUtils.hasText(pathInfo)) {
Assert.isTrue(pathInfo.startsWith("/"), "pathInfo must start with a '/'");
}
this.pathInfo = pathInfo;
return this;
}
/**
* Set the secure property of the {@link ServletRequest} indicating use of a
* secure channel, such as HTTPS.
*
* @param secure whether the request is using a secure channel
*/
public MockHttpServletRequestBuilder secure(boolean secure){
this.secure = secure;
return this;
}
/**
* An extension point for further initialization of {@link MockHttpServletRequest}
* in ways not built directly into the {@code MockHttpServletRequestBuilder}.
* Implementation of this interface can have builder-style methods themselves
* and be made accessible through static factory methods.
*
* @param postProcessor a post-processor to add
*/
public MockHttpServletRequestBuilder with(RequestPostProcessor postProcessor) {
Assert.notNull(postProcessor, "postProcessor is required");
this.postProcessors.add(postProcessor);
return this;
}
/**
* {@inheritDoc}
* @return always returns {@code true}.
*/
@Override
public boolean isMergeEnabled() {
return true;
}
/**
* Merges the properties of the "parent" RequestBuilder accepting values
* only if not already set in "this" instance.
*
* @param parent the parent {@code RequestBuilder} to inherit properties from
* @return the result of the merge
*/
@Override
public Object merge(Object parent) {
if (parent == null) {
return this;
}
if (!(parent instanceof MockHttpServletRequestBuilder)) {
throw new IllegalArgumentException("Cannot merge with [" + parent.getClass().getName() + "]");
}
MockHttpServletRequestBuilder parentBuilder = (MockHttpServletRequestBuilder) parent;
for (String headerName : parentBuilder.headers.keySet()) {
if (!this.headers.containsKey(headerName)) {
this.headers.put(headerName, parentBuilder.headers.get(headerName));
}
}
if (this.contentType == null) {
this.contentType = parentBuilder.contentType;
}
if (this.content == null) {
this.content = parentBuilder.content;
}
for (String paramName : parentBuilder.parameters.keySet()) {
if (!this.parameters.containsKey(paramName)) {
this.parameters.put(paramName, parentBuilder.parameters.get(paramName));
}
}
for (Cookie cookie : parentBuilder.cookies) {
if (!containsCookie(cookie)) {
this.cookies.add(cookie);
}
}
if (this.locale == null) {
this.locale = parentBuilder.locale;
}
if (this.characterEncoding == null) {
this.characterEncoding = parentBuilder.characterEncoding;
}
if (this.principal == null) {
this.principal = parentBuilder.principal;
}
if (this.secure == null) {
this.secure = parentBuilder.secure;
}
for (String attributeName : parentBuilder.attributes.keySet()) {
if (!this.attributes.containsKey(attributeName)) {
this.attributes.put(attributeName, parentBuilder.attributes.get(attributeName));
}
}
if (this.session == null) {
this.session = parentBuilder.session;
}
for (String sessionAttributeName : parentBuilder.sessionAttributes.keySet()) {
if (!this.sessionAttributes.containsKey(sessionAttributeName)) {
this.sessionAttributes.put(sessionAttributeName, parentBuilder.sessionAttributes.get(sessionAttributeName));
}
}
for (String flashAttributeName : parentBuilder.flashAttributes.keySet()) {
if (!this.flashAttributes.containsKey(flashAttributeName)) {
this.flashAttributes.put(flashAttributeName, parentBuilder.flashAttributes.get(flashAttributeName));
}
}
if (!StringUtils.hasText(this.contextPath)) {
this.contextPath = parentBuilder.contextPath;
}
if (!StringUtils.hasText(this.servletPath)) {
this.servletPath = parentBuilder.servletPath;
}
if (ValueConstants.DEFAULT_NONE.equals(this.pathInfo)) {
this.pathInfo = parentBuilder.pathInfo;
}
this.postProcessors.addAll(parentBuilder.postProcessors);
return this;
}
private boolean containsCookie(Cookie cookie) {
for (Cookie c : this.cookies) {
if (ObjectUtils.nullSafeEquals(c.getName(), cookie.getName())) {
return true;
}
}
return false;
}
/**
* Build a {@link MockHttpServletRequest}.
*/
@Override
public final MockHttpServletRequest buildRequest(ServletContext servletContext) {
MockHttpServletRequest request = createServletRequest(servletContext);
String requestUri = this.uriComponents.getPath();
request.setRequestURI(requestUri);
updatePathRequestProperties(request, requestUri);
if (this.uriComponents.getScheme() != null) {
request.setScheme(this.uriComponents.getScheme());
}
if (this.uriComponents.getHost() != null) {
request.setServerName(uriComponents.getHost());
}
if (this.uriComponents.getPort() != -1) {
request.setServerPort(this.uriComponents.getPort());
}
request.setMethod(this.method.name());
for (String name : this.headers.keySet()) {
for (Object value : this.headers.get(name)) {
request.addHeader(name, value);
}
}
try {
if (this.uriComponents.getQuery() != null) {
String query = UriUtils.decode(this.uriComponents.getQuery(), "UTF-8");
request.setQueryString(query);
}
for (Entry<String, List<String>> entry : this.uriComponents.getQueryParams().entrySet()) {
for (String value : entry.getValue()) {
value = (value != null) ? UriUtils.decode(value, "UTF-8") : null;
request.addParameter(UriUtils.decode(entry.getKey(), "UTF-8"), value);
}
}
}
catch (UnsupportedEncodingException ex) {
// shouldn't happen
}
for (String name : this.parameters.keySet()) {
for (String value : this.parameters.get(name)) {
request.addParameter(name, value);
}
}
request.setContentType(this.contentType);
request.setContent(this.content);
request.setCookies(this.cookies.toArray(new Cookie[this.cookies.size()]));
if (this.locale != null) {
request.addPreferredLocale(this.locale);
}
request.setCharacterEncoding(this.characterEncoding);
request.setUserPrincipal(this.principal);
if (this.secure != null) {
request.setSecure(this.secure);
}
for (String name : this.attributes.keySet()) {
request.setAttribute(name, this.attributes.get(name));
}
// Set session before session and flash attributes
if (this.session != null) {
request.setSession(this.session);
}
for (String name : this.sessionAttributes.keySet()) {
request.getSession().setAttribute(name, this.sessionAttributes.get(name));
}
FlashMap flashMap = new FlashMap();
flashMap.putAll(this.flashAttributes);
FlashMapManager flashMapManager = getFlashMapManager(request);
flashMapManager.saveOutputFlashMap(flashMap, request, new MockHttpServletResponse());
// Apply post-processors at the very end
for (RequestPostProcessor postProcessor : this.postProcessors) {
request = postProcessor.postProcessRequest(request);
Assert.notNull(request, "Post-processor [" + postProcessor.getClass().getName() + "] returned null");
}
request.setAsyncSupported(true);
return request;
}
/**
* Create a new {@link MockHttpServletRequest} based on the given
* {@link ServletContext}. Can be overridden in subclasses.
*/
protected MockHttpServletRequest createServletRequest(ServletContext servletContext) {
return new MockHttpServletRequest(servletContext);
}
/**
* Update the contextPath, servletPath, and pathInfo of the request.
*/
private void updatePathRequestProperties(MockHttpServletRequest request, String requestUri) {
Assert.isTrue(requestUri.startsWith(this.contextPath),
"requestURI [" + requestUri + "] does not start with contextPath [" + this.contextPath + "]");
request.setContextPath(this.contextPath);
request.setServletPath(this.servletPath);
if (ValueConstants.DEFAULT_NONE.equals(this.pathInfo)) {
Assert.isTrue(requestUri.startsWith(this.contextPath + this.servletPath),
"Invalid servletPath [" + this.servletPath + "] for requestURI [" + requestUri + "]");
String extraPath = requestUri.substring(this.contextPath.length() + this.servletPath.length());
this.pathInfo = (StringUtils.hasText(extraPath)) ? extraPath : null;
}
request.setPathInfo(this.pathInfo);
}
private FlashMapManager getFlashMapManager(MockHttpServletRequest request) {
FlashMapManager flashMapManager = null;
try {
ServletContext servletContext = request.getServletContext();
WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
flashMapManager = wac.getBean(DispatcherServlet.FLASH_MAP_MANAGER_BEAN_NAME, FlashMapManager.class);
}
catch (IllegalStateException ex) {
}
catch (NoSuchBeanDefinitionException ex) {
}
return (flashMapManager != null) ? flashMapManager : new SessionFlashMapManager();
}
private static <T> void addToMultiValueMap(MultiValueMap<String, T> map, String name, T[] values) {
Assert.hasLength(name, "'name' must not be empty");
Assert.notNull(values, "'values' is required");
Assert.notEmpty(values, "'values' must not be empty");
for (T value : values) {
map.add(name, value);
}
}
private static void addAttributeToMap(Map<String, Object> map, String name, Object value) {
Assert.hasLength(name, "'name' must not be empty");
Assert.notNull(value, "'value' must not be null");
map.put(name, value);
}
}

View File

@@ -0,0 +1,105 @@
/*
* Copyright 2002-2013 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.servlet.request;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.ServletContext;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.mock.web.MockMultipartHttpServletRequest;
/**
* Default builder for {@link MockMultipartHttpServletRequest}.
*
* @author Rossen Stoyanchev
* @author Arjen Poutsma
* @since 3.2
*/
public class MockMultipartHttpServletRequestBuilder extends MockHttpServletRequestBuilder {
private final List<MockMultipartFile> files = new ArrayList<MockMultipartFile>();
/**
* Package private constructor. Use static factory methods in
* {@link MockMvcRequestBuilders}.
*
* <p>For other ways to initialize a {@code MockMultipartHttpServletRequest},
* see {@link #with(RequestPostProcessor)} and the
* {@link RequestPostProcessor} extension point.
*
* @param urlTemplate a URL template; the resulting URL will be encoded
* @param urlVariables zero or more URL variables
*/
MockMultipartHttpServletRequestBuilder(String urlTemplate, Object... urlVariables) {
super(HttpMethod.POST, urlTemplate, urlVariables);
super.contentType(MediaType.MULTIPART_FORM_DATA);
}
/**
* Create a new MockMultipartFile with the given content.
*
* @param name the name of the file
* @param content the content of the file
*/
public MockMultipartHttpServletRequestBuilder file(String name, byte[] content) {
this.files.add(new MockMultipartFile(name, content));
return this;
}
/**
* Add the given MockMultipartFile.
*
* @param file the multipart file
*/
public MockMultipartHttpServletRequestBuilder file(MockMultipartFile file) {
this.files.add(file);
return this;
}
@Override
public Object merge(Object parent) {
if (parent == null) {
return this;
}
if (parent instanceof MockHttpServletRequestBuilder) {
super.merge(parent);
if (parent instanceof MockMultipartHttpServletRequestBuilder) {
MockMultipartHttpServletRequestBuilder parentBuilder = (MockMultipartHttpServletRequestBuilder) parent;
this.files.addAll(parentBuilder.files);
}
}
else {
throw new IllegalArgumentException("Cannot merge with [" + parent.getClass().getName() + "]");
}
return this;
}
@Override
protected final MockHttpServletRequest createServletRequest(ServletContext servletContext) {
MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest(servletContext);
for (MockMultipartFile file : this.files) {
request.addFile(file);
}
return request;
}
}

View File

@@ -0,0 +1,149 @@
/*
* Copyright 2002-2013 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.servlet.request;
import java.lang.reflect.Method;
import javax.servlet.ServletContext;
import org.springframework.http.HttpMethod;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.RequestBuilder;
import org.springframework.util.ReflectionUtils;
/**
* Static factory methods for {@link RequestBuilder}s.
*
* <p><strong>Eclipse users:</strong> consider adding this class as a Java
* editor favorite. To navigate, open the Preferences and type "favorites".
*
* @author Arjen Poutsma
* @author Rossen Stoyanchev
* @since 3.2
*/
public abstract class MockMvcRequestBuilders {
private MockMvcRequestBuilders() {
}
/**
* Create a {@link MockHttpServletRequestBuilder} for a GET request.
*
* @param urlTemplate a URL template; the resulting URL will be encoded
* @param urlVariables zero or more URL variables
*/
public static MockHttpServletRequestBuilder get(String urlTemplate, Object... urlVariables) {
return new MockHttpServletRequestBuilder(HttpMethod.GET, urlTemplate, urlVariables);
}
/**
* Create a {@link MockHttpServletRequestBuilder} for a POST request.
*
* @param urlTemplate a URL template; the resulting URL will be encoded
* @param urlVariables zero or more URL variables
*/
public static MockHttpServletRequestBuilder post(String urlTemplate, Object... urlVariables) {
return new MockHttpServletRequestBuilder(HttpMethod.POST, urlTemplate, urlVariables);
}
/**
* Create a {@link MockHttpServletRequestBuilder} for a PUT request.
*
* @param urlTemplate a URL template; the resulting URL will be encoded
* @param urlVariables zero or more URL variables
*/
public static MockHttpServletRequestBuilder put(String urlTemplate, Object... urlVariables) {
return new MockHttpServletRequestBuilder(HttpMethod.PUT, urlTemplate, urlVariables);
}
/**
* Create a {@link MockHttpServletRequestBuilder} for a DELETE request.
*
* @param urlTemplate a URL template; the resulting URL will be encoded
* @param urlVariables zero or more URL variables
*/
public static MockHttpServletRequestBuilder delete(String urlTemplate, Object... urlVariables) {
return new MockHttpServletRequestBuilder(HttpMethod.DELETE, urlTemplate, urlVariables);
}
/**
* Create a {@link MockHttpServletRequestBuilder} for an OPTIONS request.
*
* @param urlTemplate a URL template; the resulting URL will be encoded
* @param urlVariables zero or more URL variables
*/
public static MockHttpServletRequestBuilder options(String urlTemplate, Object... urlVariables) {
return new MockHttpServletRequestBuilder(HttpMethod.OPTIONS, urlTemplate, urlVariables);
}
/**
* Create a {@link MockHttpServletRequestBuilder} for a request with the given HTTP method.
*
* @param httpMethod the HTTP method
* @param urlTemplate a URL template; the resulting URL will be encoded
* @param urlVariables zero or more URL variables
*/
public static MockHttpServletRequestBuilder request(HttpMethod httpMethod, String urlTemplate, Object... urlVariables) {
return new MockHttpServletRequestBuilder(httpMethod, urlTemplate, urlVariables);
}
/**
* Create a {@link MockHttpServletRequestBuilder} for a multipart request.
*
* @param urlTemplate a URL template; the resulting URL will be encoded
* @param urlVariables zero or more URL variables
*/
public static MockMultipartHttpServletRequestBuilder fileUpload(String urlTemplate, Object... urlVariables) {
return new MockMultipartHttpServletRequestBuilder(urlTemplate, urlVariables);
}
/**
* Create a {@link RequestBuilder} for an async dispatch from the
* {@link MvcResult} of the request that started async processing.
*
* <p>Usage involves performing one request first that starts async processing:
* <pre class="code">
* MvcResult mvcResult = this.mockMvc.perform(get("/1"))
* .andExpect(request().asyncStarted())
* .andReturn();
* </pre>
*
* <p>And then performing the async dispatch re-using the {@code MvcResult}:
* <pre class="code">
* this.mockMvc.perform(asyncDispatch(mvcResult))
* .andExpect(status().isOk())
* .andExpect(content().contentType(MediaType.APPLICATION_JSON))
* .andExpect(content().string("{\"name\":\"Joe\",\"someDouble\":0.0,\"someBoolean\":false}"));
* </pre>
*
* @param mvcResult the result from the request that started async processing
*/
public static RequestBuilder asyncDispatch(final MvcResult mvcResult) {
return new RequestBuilder() {
@Override
public MockHttpServletRequest buildRequest(ServletContext servletContext) {
MockHttpServletRequest request = mvcResult.getRequest();
Method method = ReflectionUtils.findMethod(request.getClass(), "setAsyncStarted", boolean.class);
method.setAccessible(true);
ReflectionUtils.invokeMethod(method, request, false);
return request;
}
};
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2002-2012 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.servlet.request;
import org.springframework.mock.web.MockHttpServletRequest;
/**
* Extension point for applications or 3rd party libraries that wish to further
* initialize a {@link MockHttpServletRequest} instance after it has been built
* by {@link MockHttpServletRequestBuilder} or its sub-class
* {@link MockMultipartHttpServletRequestBuilder}.
*
* <p>Implementations of this interface can be provided to
* {@link MockHttpServletRequestBuilder#with(RequestPostProcessor)} at the time
* when a request is about to be performed.
*
* @author Rossen Stoyanchev
* @author Rob Winch
* @since 3.2
*/
public interface RequestPostProcessor {
/**
* Post-process the given {@code MockHttpServletRequest} after its creation
* and initialization through a {@code MockHttpServletRequestBuilder}.
*
* @param request the request to initialize
* @return the request to use, either the one passed in or a wrapped one;
*/
MockHttpServletRequest postProcessRequest(MockHttpServletRequest request);
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright 2002-2012 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.
*/
/**
* Contains built-in {@link org.springframework.test.web.servlet.RequestBuilder}
* implementations. Use
* {@link org.springframework.test.web.servlet.request.MockMvcRequestBuilders}
* to gain access to instances of those implementations.
*/
package org.springframework.test.web.servlet.request;

View File

@@ -0,0 +1,213 @@
/*
* Copyright 2002-2013 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.servlet.result;
import static org.springframework.test.util.AssertionErrors.assertEquals;
import static org.springframework.test.util.MatcherAssertionErrors.assertThat;
import static org.springframework.test.util.AssertionErrors.assertTrue;
import java.util.Map;
import javax.servlet.http.HttpServletResponse;
import javax.xml.transform.Source;
import javax.xml.transform.dom.DOMSource;
import org.hamcrest.Matcher;
import org.springframework.http.MediaType;
import org.springframework.test.util.XmlExpectationsHelper;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultMatcher;
import org.w3c.dom.Node;
/**
* Factory for response content assertions. An instance of this class is
* typically accessed via {@link MockMvcResultMatchers#content()}.
*
* @author Rossen Stoyanchev
* @since 3.2
*/
public class ContentResultMatchers {
private final XmlExpectationsHelper xmlHelper;
/**
* Protected constructor.
* Use {@link MockMvcResultMatchers#content()}.
*/
protected ContentResultMatchers() {
this.xmlHelper = new XmlExpectationsHelper();
}
/**
* Assert the ServletResponse content type. The given content type must
* fully match including type, sub-type, and parameters. For checking
* only the type and sub-type see {@link #contentTypeCompatibleWith(String)}.
*/
public ResultMatcher contentType(String contentType) {
return contentType(MediaType.parseMediaType(contentType));
}
/**
* Assert the ServletResponse content type after parsing it as a MediaType.
* The given content type must fully match including type, sub-type, and
* parameters. For checking only the type and sub-type see
* {@link #contentTypeCompatibleWith(MediaType)}.
*/
public ResultMatcher contentType(final MediaType contentType) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
String actual = result.getResponse().getContentType();
assertTrue("Content type not set", actual != null);
assertEquals("Content type", contentType, MediaType.parseMediaType(actual));
}
};
}
/**
* Assert the ServletResponse content type is compatible with the given
* content type as defined by {@link MediaType#isCompatibleWith(MediaType)}.
*/
public ResultMatcher contentTypeCompatibleWith(String contentType) {
return contentTypeCompatibleWith(MediaType.parseMediaType(contentType));
}
/**
* Assert the ServletResponse content type is compatible with the given
* content type as defined by {@link MediaType#isCompatibleWith(MediaType)}.
*/
public ResultMatcher contentTypeCompatibleWith(final MediaType contentType) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
String actual = result.getResponse().getContentType();
assertTrue("Content type not set", actual != null);
MediaType actualContentType = MediaType.parseMediaType(actual);
assertTrue("Content type [" + actual + "] is not compatible with [" + contentType + "]",
actualContentType.isCompatibleWith(contentType));
}
};
}
/**
* Assert the character encoding in the ServletResponse.
* @see HttpServletResponse#getCharacterEncoding()
*/
public ResultMatcher encoding(final String characterEncoding) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) {
String actual = result.getResponse().getCharacterEncoding();
assertEquals("Character encoding", characterEncoding, actual);
}
};
}
/**
* Assert the response body content with a Hamcrest {@link Matcher}.
* <pre class="code">
* mockMvc.perform(get("/path"))
* .andExpect(content(containsString("text")));
* </pre>
*/
public ResultMatcher string(final Matcher<? super String> matcher) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
assertThat("Response content", result.getResponse().getContentAsString(), matcher);
}
};
}
/**
* Assert the response body content as a String.
*/
public ResultMatcher string(final String expectedContent) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
assertEquals("Response content", expectedContent, result.getResponse().getContentAsString());
}
};
}
/**
* Assert the response body content as a byte array.
*/
public ResultMatcher bytes(final byte[] expectedContent) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
assertEquals("Response content", expectedContent, result.getResponse().getContentAsByteArray());
}
};
}
/**
* Parse the response content and the given string as XML and assert the two
* are "similar" - i.e. they contain the same elements and attributes
* regardless of order.
*
* <p>Use of this matcher requires the <a
* href="http://xmlunit.sourceforge.net/">XMLUnit<a/> library.
*
* @param xmlContent the expected XML content
* @see MockMvcResultMatchers#xpath(String, Object...)
* @see MockMvcResultMatchers#xpath(String, Map, Object...)
*/
public ResultMatcher xml(final String xmlContent) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
String content = result.getResponse().getContentAsString();
xmlHelper.assertXmlEqual(xmlContent, content);
}
};
}
/**
* Parse the response content as {@link Node} and apply the given Hamcrest
* {@link Matcher}.
*/
public ResultMatcher node(final Matcher<? super Node> matcher) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
String content = result.getResponse().getContentAsString();
xmlHelper.assertNode(content, matcher);
}
};
}
/**
* Parse the response content as {@link DOMSource} and apply the given
* Hamcrest {@link Matcher}.
*
* @see <a href="http://code.google.com/p/xml-matchers/">xml-matchers</a>
*/
public ResultMatcher source(final Matcher<? super Source> matcher) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
String content = result.getResponse().getContentAsString();
xmlHelper.assertSource(content, matcher);
}
};
}
}

View File

@@ -0,0 +1,244 @@
/*
* Copyright 2002-2012 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.servlet.result;
import static org.springframework.test.util.AssertionErrors.assertEquals;
import static org.springframework.test.util.MatcherAssertionErrors.assertThat;
import static org.springframework.test.util.AssertionErrors.assertTrue;
import javax.servlet.http.Cookie;
import org.hamcrest.Matcher;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultMatcher;
/**
* Factory for response cookie assertions. An instance of this class is
* typically accessed via {@link MockMvcResultMatchers#cookie()}.
*
* @author Rossen Stoyanchev
* @author Thomas Bruyelle
* @since 3.2
*/
public class CookieResultMatchers {
/**
* Protected constructor.
* Use {@link MockMvcResultMatchers#cookie()}.
*/
protected CookieResultMatchers() {
}
/**
* Assert a cookie value with the given Hamcrest {@link Matcher}.
*/
public ResultMatcher value(final String name, final Matcher<? super String> matcher) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) {
Cookie cookie = result.getResponse().getCookie(name);
assertTrue("Response cookie not found: " + name, cookie != null);
assertThat("Response cookie", cookie.getValue(), matcher);
}
};
}
/**
* Assert a cookie value.
*/
public ResultMatcher value(final String name, final String expectedValue) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) {
Cookie cookie = result.getResponse().getCookie(name);
assertTrue("Response cookie not found: " + name, cookie != null);
assertEquals("Response cookie", expectedValue, cookie.getValue());
}
};
}
/**
* Assert a cookie exists. The existence check is irrespective of whether
* max age is 0 (i.e. expired).
*/
public ResultMatcher exists(final String name) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) {
Cookie cookie = result.getResponse().getCookie(name);
assertTrue("No cookie with name: " + name, cookie != null);
}
};
}
/**
* Assert a cookie does not exist. Note that the existence check is
* irrespective of whether max age is 0, i.e. expired.
*/
public ResultMatcher doesNotExist(final String name) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) {
Cookie cookie = result.getResponse().getCookie(name);
assertTrue("Unexpected cookie with name " + name, cookie == null);
}
};
}
/**
* Assert a cookie's maxAge with a Hamcrest {@link Matcher}.
*/
public ResultMatcher maxAge(final String name, final Matcher<? super Integer> matcher) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) {
Cookie cookie = result.getResponse().getCookie(name);
assertTrue("No cookie with name: " + name, cookie != null);
assertThat("Response cookie maxAge", cookie.getMaxAge(), matcher);
}
};
}
/**
* Assert a cookie's maxAge value.
*/
public ResultMatcher maxAge(final String name, final int maxAge) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) {
Cookie cookie = result.getResponse().getCookie(name);
assertTrue("No cookie with name: " + name, cookie != null);
assertEquals("Response cookie maxAge", maxAge, cookie.getMaxAge());
}
};
}
/**
* Assert a cookie path with a Hamcrest {@link Matcher}.
*/
public ResultMatcher path(final String name, final Matcher<? super String> matcher) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
Cookie cookie = result.getResponse().getCookie(name);
assertThat("Response cookie path", cookie.getPath(), matcher);
}
};
}
public ResultMatcher path(final String name, final String path) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
Cookie cookie = result.getResponse().getCookie(name);
assertEquals("Response cookie path", path, cookie.getPath());
}
};
}
/**
* Assert a cookie's domain with a Hamcrest {@link Matcher}.
*/
public ResultMatcher domain(final String name, final Matcher<? super String> matcher) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
Cookie cookie = result.getResponse().getCookie(name);
assertThat("Response cookie domain", cookie.getDomain(), matcher);
}
};
}
/**
* Assert a cookie's domain value.
*/
public ResultMatcher domain(final String name, final String domain) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
Cookie cookie = result.getResponse().getCookie(name);
assertEquals("Response cookie domain", domain, cookie.getDomain());
}
};
}
/**
* Assert a cookie's comment with a Hamcrest {@link Matcher}.
*/
public ResultMatcher comment(final String name, final Matcher<? super String> matcher) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
Cookie cookie = result.getResponse().getCookie(name);
assertThat("Response cookie comment", cookie.getComment(), matcher);
}
};
}
/**
* Assert a cookie's comment value.
*/
public ResultMatcher comment(final String name, final String comment) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
Cookie cookie = result.getResponse().getCookie(name);
assertEquals("Response cookie comment", comment, cookie.getComment());
}
};
}
/**
* Assert a cookie's version with a Hamcrest {@link Matcher}
*/
public ResultMatcher version(final String name, final Matcher<? super Integer> matcher) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
Cookie cookie = result.getResponse().getCookie(name);
assertThat("Response cookie version", cookie.getVersion(), matcher);
}
};
}
/**
* Assert a cookie's version value.
*/
public ResultMatcher version(final String name, final int version) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
Cookie cookie = result.getResponse().getCookie(name);
assertEquals("Response cookie version", version, cookie.getVersion());
}
};
}
/**
* Assert whether the cookie must be sent over a secure protocol or not.
*/
public ResultMatcher secure(final String name, final boolean secure) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
Cookie cookie = result.getResponse().getCookie(name);
assertEquals("Response cookie secure", secure, cookie.getSecure());
}
};
}
}

View File

@@ -0,0 +1,94 @@
/*
* Copyright 2002-2012 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.servlet.result;
import static org.springframework.test.util.AssertionErrors.*;
import static org.springframework.test.util.MatcherAssertionErrors.assertThat;
import org.hamcrest.Matcher;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultMatcher;
/**
* Factory for "output" flash attribute assertions. An instance of this class is
* typically accessed via {@link MockMvcResultMatchers#flash()}.
*
* @author Rossen Stoyanchev
* @since 3.2
*/
public class FlashAttributeResultMatchers {
/**
* Protected constructor.
* Use {@link MockMvcResultMatchers#flash()}.
*/
protected FlashAttributeResultMatchers() {
}
/**
* Assert a flash attribute's value with the given Hamcrest {@link Matcher}.
*/
public <T> ResultMatcher attribute(final String name, final Matcher<T> matcher) {
return new ResultMatcher() {
@Override
@SuppressWarnings("unchecked")
public void match(MvcResult result) throws Exception {
assertThat("Flash attribute", (T) result.getFlashMap().get(name), matcher);
}
};
}
/**
* Assert a flash attribute's value.
*/
public <T> ResultMatcher attribute(final String name, final Object value) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
assertEquals("Flash attribute", value, result.getFlashMap().get(name));
}
};
}
/**
* Assert the existence of the given flash attributes.
*/
public <T> ResultMatcher attributeExists(final String... names) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
for (String name : names) {
assertTrue("Flash attribute [" + name + "] does not exist", result.getFlashMap().get(name) != null);
}
}
};
}
/**
* Assert the number of flash attributes.
*/
public <T> ResultMatcher attributeCount(final int count) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
assertEquals("FlashMap size", count, result.getFlashMap().size());
}
};
}
}

View File

@@ -0,0 +1,124 @@
/*
* Copyright 2002-2012 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.servlet.result;
import static org.springframework.test.util.AssertionErrors.assertEquals;
import static org.springframework.test.util.MatcherAssertionErrors.assertThat;
import static org.springframework.test.util.AssertionErrors.assertTrue;
import java.lang.reflect.Method;
import org.hamcrest.Matcher;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultMatcher;
import org.springframework.util.ClassUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
/**
* Factory for assertions on the selected handler. An instance of this class is
* typically accessed via {@link MockMvcResultMatchers#handler()}.
*
* @author Rossen Stoyanchev
* @since 3.2
*/
public class HandlerResultMatchers {
/**
* Protected constructor.
* Use {@link MockMvcResultMatchers#handler()}.
*/
protected HandlerResultMatchers() {
}
/**
* Assert the type of the handler that processed the request.
*/
public ResultMatcher handlerType(final Class<?> type) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
Object handler = result.getHandler();
assertTrue("No handler: ", handler != null);
Class<?> actual = handler.getClass();
if (HandlerMethod.class.isInstance(handler)) {
actual = ((HandlerMethod) handler).getBeanType();
}
assertEquals("Handler type", type, ClassUtils.getUserClass(actual));
}
};
}
/**
* Assert the name of the controller method that processed the request with
* the given Hamcrest {@link Matcher}.
*
* <p>Use of this method implies annotated controllers are processed with
* {@link RequestMappingHandlerMapping} and {@link RequestMappingHandlerAdapter}.
*/
public ResultMatcher methodName(final Matcher<? super String> matcher) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
Object handler = assertHandlerMethod(result);
assertThat("HandlerMethod", ((HandlerMethod) handler).getMethod().getName(), matcher);
}
};
}
/**
* Assert the name of the controller method that processed the request.
*
* <p>Use of this method implies annotated controllers are processed with
* {@link RequestMappingHandlerMapping} and {@link RequestMappingHandlerAdapter}.
*/
public ResultMatcher methodName(final String name) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
Object handler = assertHandlerMethod(result);
assertEquals("HandlerMethod", name, ((HandlerMethod) handler).getMethod().getName());
}
};
}
/**
* Assert the controller method that processed the request.
*
* <p>Use of this method implies annotated controllers are processed with
* {@link RequestMappingHandlerMapping} and {@link RequestMappingHandlerAdapter}.
*/
public ResultMatcher method(final Method method) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
Object handler = assertHandlerMethod(result);
assertEquals("HandlerMethod", method, ((HandlerMethod) handler).getMethod());
}
};
}
private static Object assertHandlerMethod(MvcResult result) {
Object handler = result.getHandler();
assertTrue("No handler: ", handler != null);
assertTrue("Not a HandlerMethod: " + handler, HandlerMethod.class.isInstance(handler));
return handler;
}
}

View File

@@ -0,0 +1,103 @@
/*
* Copyright 2002-2013 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.servlet.result;
import org.hamcrest.Matcher;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultMatcher;
import static org.springframework.test.util.AssertionErrors.*;
import static org.springframework.test.util.MatcherAssertionErrors.*;
/**
* Factory for response header assertions. An instance of this
* class is usually accessed via {@link MockMvcResultMatchers#header()}.
*
* @author Rossen Stoyanchev
* @author Sam Brannen
* @since 3.2
*/
public class HeaderResultMatchers {
/**
* Protected constructor.
* Use {@link MockMvcResultMatchers#header()}.
*/
protected HeaderResultMatchers() {
}
/**
* Assert the primary value of the named response header with the given
* Hamcrest {@link Matcher}.
*/
public ResultMatcher string(final String name, final Matcher<? super String> matcher) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) {
assertThat("Response header " + name, result.getResponse().getHeader(name), matcher);
}
};
}
/**
* Assert the primary value of the named response header as a {@link String}.
*/
public ResultMatcher string(final String name, final String value) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) {
assertEquals("Response header " + name, value, result.getResponse().getHeader(name));
}
};
}
/**
* Assert that the named response header does not exist.
* @since 4.0
*/
public ResultMatcher doesNotExist(final String name) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) {
assertTrue("Response should not contain header " + name, !result.getResponse().containsHeader(name));
}
};
}
/**
* Assert the primary value of the named response header as a {@code long}.
*
* <p>The {@link ResultMatcher} returned by this method throws an {@link AssertionError}
* if the response does not contain the specified header, or if the supplied
* {@code value} does not match the primary value.
*/
public ResultMatcher longValue(final String name, final long value) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) {
assertTrue("Response does not contain header " + name, result.getResponse().containsHeader(name));
assertEquals("Response header " + name, value, Long.parseLong(result.getResponse().getHeader(name)));
}
};
}
}

View File

@@ -0,0 +1,110 @@
/*
* Copyright 2002-2012 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.servlet.result;
import org.hamcrest.Matcher;
import org.springframework.test.util.JsonPathExpectationsHelper;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultMatcher;
/**
* Factory for assertions on the response content using <a
* href="http://goessner.net/articles/JsonPath/">JSONPath</a> expressions.
* An instance of this class is typically accessed via
* {@link MockMvcResultMatchers#jsonPath}.
*
* @author Rossen Stoyanchev
* @since 3.2
*/
public class JsonPathResultMatchers {
private JsonPathExpectationsHelper jsonPathHelper;
/**
* Protected constructor. Use
* {@link MockMvcResultMatchers#jsonPath(String, Object...)} or
* {@link MockMvcResultMatchers#jsonPath(String, Matcher)}.
*/
protected JsonPathResultMatchers(String expression, Object ... args) {
this.jsonPathHelper = new JsonPathExpectationsHelper(expression, args);
}
/**
* Evaluate the JSONPath and assert the value of the content found with the
* given Hamcrest {@code Matcher}.
*/
public <T> ResultMatcher value(final Matcher<T> matcher) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
String content = result.getResponse().getContentAsString();
jsonPathHelper.assertValue(content, matcher);
}
};
}
/**
* Evaluate the JSONPath and assert the value of the content found.
*/
public ResultMatcher value(final Object expectedValue) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
jsonPathHelper.assertValue(result.getResponse().getContentAsString(), expectedValue);
}
};
}
/**
* Evaluate the JSONPath and assert that content exists.
*/
public ResultMatcher exists() {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
String content = result.getResponse().getContentAsString();
jsonPathHelper.exists(content);
}
};
}
/**
* Evaluate the JSON path and assert not content was found.
*/
public ResultMatcher doesNotExist() {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
String content = result.getResponse().getContentAsString();
jsonPathHelper.doesNotExist(content);
}
};
}
/**
* Evluate the JSON path and assert the content found is an array.
*/
public ResultMatcher isArray() {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
String content = result.getResponse().getContentAsString();
jsonPathHelper.assertValueIsArray(content);
}
};
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright 2002-2012 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.servlet.result;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultHandler;
import org.springframework.util.CollectionUtils;
/**
* Static, factory methods for {@link ResultHandler}-based result actions.
*
* <p><strong>Eclipse users:</strong> consider adding this class as a Java editor
* favorite. To navigate, open the Preferences and type "favorites".
*
* @author Rossen Stoyanchev
* @since 3.2
*/
public abstract class MockMvcResultHandlers {
private MockMvcResultHandlers() {
}
/**
* Print {@link MvcResult} details to the "standard" output stream.
*/
public static ResultHandler print() {
return new ConsolePrintingResultHandler();
}
/** An {@link PrintingResultHandler} that writes to the "standard" output stream */
private static class ConsolePrintingResultHandler extends PrintingResultHandler {
public ConsolePrintingResultHandler() {
super(new ResultValuePrinter() {
@Override
public void printHeading(String heading) {
System.out.println();
System.out.println(String.format("%20s:", heading));
}
@Override
public void printValue(String label, Object value) {
if (value != null && value.getClass().isArray()) {
value = CollectionUtils.arrayToList(value);
}
System.out.println(String.format("%20s = %s", label, value));
}
});
}
}
}

View File

@@ -0,0 +1,237 @@
/*
* Copyright 2002-2013 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.servlet.result;
import java.util.Map;
import javax.xml.xpath.XPathExpressionException;
import org.hamcrest.Matcher;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultMatcher;
import org.springframework.util.AntPathMatcher;
import static org.springframework.test.util.AssertionErrors.*;
/**
* Static, factory methods for {@link ResultMatcher}-based result actions.
*
* <p><strong>Eclipse users:</strong> consider adding this class as a Java editor
* favorite. To navigate, open the Preferences and type "favorites".
*
* @author Rossen Stoyanchev
* @author Brian Clozel
* @since 3.2
*/
public abstract class MockMvcResultMatchers {
private static final AntPathMatcher pathMatcher = new AntPathMatcher();
private MockMvcResultMatchers() {
}
/**
* Access to request-related assertions.
*/
public static RequestResultMatchers request() {
return new RequestResultMatchers();
}
/**
* Access to assertions for the handler that handled the request.
*/
public static HandlerResultMatchers handler() {
return new HandlerResultMatchers();
}
/**
* Access to model-related assertions.
*/
public static ModelResultMatchers model() {
return new ModelResultMatchers();
}
/**
* Access to assertions on the selected view.
*/
public static ViewResultMatchers view() {
return new ViewResultMatchers();
}
/**
* Access to flash attribute assertions.
*/
public static FlashAttributeResultMatchers flash() {
return new FlashAttributeResultMatchers();
}
/**
* Asserts the request was forwarded to the given URL.
* This methods accepts only exact matches.
* @param expectedUrl the exact URL expected
*/
public static ResultMatcher forwardedUrl(final String expectedUrl) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) {
assertEquals("Forwarded URL", expectedUrl, result.getResponse().getForwardedUrl());
}
};
}
/**
* Asserts the request was forwarded to the given URL.
* This methods accepts {@link org.springframework.util.AntPathMatcher} expressions.
*
* @param urlPattern an AntPath expression to match against
* @see org.springframework.util.AntPathMatcher
* @since 4.0
*/
public static ResultMatcher forwardedUrlPattern(final String urlPattern) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) {
assertTrue("AntPath expression", pathMatcher.isPattern(urlPattern));
assertTrue("Forwarded URL does not match the expected URL pattern",
pathMatcher.match(urlPattern, result.getResponse().getForwardedUrl()));
}
};
}
/**
* Asserts the request was redirected to the given URL.
* This methods accepts only exact matches.
* @param expectedUrl the exact URL expected
*/
public static ResultMatcher redirectedUrl(final String expectedUrl) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) {
assertEquals("Redirected URL", expectedUrl, result.getResponse().getRedirectedUrl());
}
};
}
/**
* Asserts the request was redirected to the given URL.
* This methods accepts {@link org.springframework.util.AntPathMatcher} expressions.
*
* @param expectedUrl an AntPath expression to match against
* @see org.springframework.util.AntPathMatcher
* @since 4.0
*/
public static ResultMatcher redirectedUrlPattern(final String expectedUrl) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) {
assertTrue("AntPath expression",pathMatcher.isPattern(expectedUrl));
assertTrue("Redirected URL",
pathMatcher.match(expectedUrl, result.getResponse().getRedirectedUrl()));
}
};
}
/**
* Access to response status assertions.
*/
public static StatusResultMatchers status() {
return new StatusResultMatchers();
}
/**
* Access to response header assertions.
*/
public static HeaderResultMatchers header() {
return new HeaderResultMatchers();
}
/**
* Access to response body assertions.
*/
public static ContentResultMatchers content() {
return new ContentResultMatchers();
}
/**
* Access to response body assertions using a <a
* href="http://goessner.net/articles/JsonPath/">JSONPath</a> expression to
* inspect a specific subset of the body. The JSON path expression can be a
* parameterized string using formatting specifiers as defined in
* {@link String#format(String, Object...)}.
*
* @param expression the JSON path optionally parameterized with arguments
* @param args arguments to parameterize the JSON path expression with
*/
public static JsonPathResultMatchers jsonPath(String expression, Object ... args) {
return new JsonPathResultMatchers(expression, args);
}
/**
* Access to response body assertions using a <a
* href="http://goessner.net/articles/JsonPath/">JSONPath</a> expression to
* inspect a specific subset of the body and a Hamcrest match for asserting
* the value found at the JSON path.
*
* @param expression the JSON path expression
* @param matcher a matcher for the value expected at the JSON path
*/
public static <T> ResultMatcher jsonPath(String expression, Matcher<T> matcher) {
return new JsonPathResultMatchers(expression).value(matcher);
}
/**
* Access to response body assertions using an XPath to inspect a specific
* subset of the body. The XPath expression can be a parameterized string
* using formatting specifiers as defined in
* {@link String#format(String, Object...)}.
*
* @param expression the XPath optionally parameterized with arguments
* @param args arguments to parameterize the XPath expression with
*/
public static XpathResultMatchers xpath(String expression, Object... args) throws XPathExpressionException {
return new XpathResultMatchers(expression, null, args);
}
/**
* Access to response body assertions using an XPath to inspect a specific
* subset of the body. The XPath expression can be a parameterized string
* using formatting specifiers as defined in
* {@link String#format(String, Object...)}.
*
* @param expression the XPath optionally parameterized with arguments
* @param namespaces namespaces referenced in the XPath expression
* @param args arguments to parameterize the XPath expression with
*/
public static XpathResultMatchers xpath(String expression, Map<String, String> namespaces, Object... args)
throws XPathExpressionException {
return new XpathResultMatchers(expression, namespaces, args);
}
/**
* Access to response cookie assertions.
*/
public static CookieResultMatchers cookie() {
return new CookieResultMatchers();
}
}

View File

@@ -0,0 +1,255 @@
/*
* Copyright 2002-2013 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.servlet.result;
import static org.springframework.test.util.AssertionErrors.assertEquals;
import static org.springframework.test.util.AssertionErrors.assertTrue;
import static org.springframework.test.util.MatcherAssertionErrors.assertThat;
import org.hamcrest.Matcher;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultMatcher;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.validation.Errors;
import org.springframework.web.servlet.ModelAndView;
/**
* Factory for assertions on the model. An instance of this class is
* typically accessed via {@link MockMvcResultMatchers#model()}.
*
* @author Rossen Stoyanchev
* @since 3.2
*/
public class ModelResultMatchers {
/**
* Protected constructor.
* Use {@link MockMvcResultMatchers#model()}.
*/
protected ModelResultMatchers() {
}
/**
* Assert a model attribute value with the given Hamcrest {@link Matcher}.
*/
public <T> ResultMatcher attribute(final String name, final Matcher<T> matcher) {
return new ResultMatcher() {
@Override
@SuppressWarnings("unchecked")
public void match(MvcResult result) throws Exception {
ModelAndView mav = getModelAndView(result);
assertThat("Model attribute '" + name + "'", (T) mav.getModel().get(name), matcher);
}
};
}
/**
* Assert a model attribute value.
*/
public ResultMatcher attribute(final String name, final Object value) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
ModelAndView mav = getModelAndView(result);
assertEquals("Model attribute '" + name + "'", value, mav.getModel().get(name));
}
};
}
/**
* Assert the given model attributes exist.
*/
public ResultMatcher attributeExists(final String... names) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
ModelAndView mav = getModelAndView(result);
for (String name : names) {
assertTrue("Model attribute '" + name + "' does not exist", mav.getModel().get(name) != null);
}
}
};
}
/**
* Assert the given model attributes do not exist
*/
public ResultMatcher attributeDoesNotExist(final String... names) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
ModelAndView mav = getModelAndView(result);
for (String name : names) {
assertTrue("Model attribute '" + name + "' exists", mav.getModel().get(name) == null);
}
}
};
}
/**
* Assert the given model attribute(s) have errors.
*/
public ResultMatcher attributeErrorCount(final String name, final int expectedCount) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
ModelAndView mav = getModelAndView(result);
Errors errors = getBindingResult(mav, name);
assertEquals("Binding/validation error count for attribute [" + name + "], ",
expectedCount, errors.getErrorCount());
}
};
}
/**
* Assert the given model attribute(s) have errors.
*/
public ResultMatcher attributeHasErrors(final String... names) {
return new ResultMatcher() {
@Override
public void match(MvcResult mvcResult) throws Exception {
ModelAndView mav = getModelAndView(mvcResult);
for (String name : names) {
BindingResult result = getBindingResult(mav, name);
assertTrue("No errors for attribute [" + name + "]", result.hasErrors());
}
}
};
}
/**
* Assert the given model attribute(s) do not have errors.
*/
public ResultMatcher attributeHasNoErrors(final String... names) {
return new ResultMatcher() {
@Override
public void match(MvcResult mvcResult) throws Exception {
ModelAndView mav = getModelAndView(mvcResult);
for (String name : names) {
BindingResult result = getBindingResult(mav, name);
assertTrue("No errors for attribute [" + name + "]", !result.hasErrors());
}
}
};
}
/**
* Assert the given model attribute field(s) have errors.
*/
public ResultMatcher attributeHasFieldErrors(final String name, final String... fieldNames) {
return new ResultMatcher() {
@Override
public void match(MvcResult mvcResult) throws Exception {
ModelAndView mav = getModelAndView(mvcResult);
BindingResult result = getBindingResult(mav, name);
assertTrue("No errors for attribute: [" + name + "]", result.hasErrors());
for (final String fieldName : fieldNames) {
assertTrue("No errors for field: [" + fieldName + "] of attribute [" + name + "]",
result.hasFieldErrors(fieldName));
}
}
};
}
/**
* Assert the total number of errors in the model.
*/
public <T> ResultMatcher errorCount(final int expectedCount) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
int actualCount = getErrorCount(getModelAndView(result).getModelMap());
assertEquals("Binding/validation error count", expectedCount, actualCount);
}
};
}
/**
* Assert the model has errors.
*/
public <T> ResultMatcher hasErrors() {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
int count = getErrorCount(getModelAndView(result).getModelMap());
assertTrue("Expected binding/validation errors", count != 0);
}
};
}
/**
* Assert the model has no errors.
*/
public <T> ResultMatcher hasNoErrors() {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
ModelAndView mav = getModelAndView(result);
for (Object value : mav.getModel().values()) {
if (value instanceof Errors) {
assertTrue("Unexpected binding/validation error(s) [" + value + "]",
!((Errors) value).hasErrors());
}
}
}
};
}
/**
* Assert the number of model attributes.
*/
public <T> ResultMatcher size(final int size) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
ModelAndView mav = getModelAndView(result);
int actual = 0;
for (String key : mav.getModel().keySet()) {
if (!key.startsWith(BindingResult.MODEL_KEY_PREFIX)) {
actual++;
}
}
assertEquals("Model size", size, actual);
}
};
}
private ModelAndView getModelAndView(MvcResult mvcResult) {
ModelAndView mav = mvcResult.getModelAndView();
assertTrue("No ModelAndView found", mav != null);
return mav;
}
private BindingResult getBindingResult(ModelAndView mav, String name) {
BindingResult result = (BindingResult) mav.getModel().get(BindingResult.MODEL_KEY_PREFIX + name);
assertTrue("No BindingResult for attribute: " + name, result != null);
return result;
}
private int getErrorCount(ModelMap model) {
int count = 0;
for (Object value : model.values()) {
if (value instanceof Errors) {
count += ((Errors) value).getErrorCount();
}
}
return count;
}
}

View File

@@ -0,0 +1,236 @@
/*
* Copyright 2002-2012 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.servlet.result;
import java.util.Enumeration;
import java.util.Map;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import org.springframework.http.HttpHeaders;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultHandler;
import org.springframework.util.ClassUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.validation.BindingResult;
import org.springframework.validation.Errors;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.FlashMap;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.support.RequestContextUtils;
/**
* Result handler that prints {@link MvcResult} details to the "standard" output
* stream. An instance of this class is typically accessed via
* {@link MockMvcResultHandlers#print()}.
*
* @author Rossen Stoyanchev
* @since 3.2
*/
public class PrintingResultHandler implements ResultHandler {
private static final boolean servlet3Present = ClassUtils.hasMethod(ServletRequest.class, "startAsync");
private final ResultValuePrinter printer;
/**
* Protected constructor.
* @param printer a {@link ResultValuePrinter} to do the actual writing
*/
protected PrintingResultHandler(ResultValuePrinter printer) {
this.printer = printer;
}
/**
* @return the result value printer.
*/
protected ResultValuePrinter getPrinter() {
return this.printer;
}
/**
* Print {@link MvcResult} details to the "standard" output stream.
*/
@Override
public final void handle(MvcResult result) throws Exception {
this.printer.printHeading("MockHttpServletRequest");
printRequest(result.getRequest());
this.printer.printHeading("Handler");
printHandler(result.getHandler(), result.getInterceptors());
if (servlet3Present) {
this.printer.printHeading("Async");
printAsyncResult(result);
}
this.printer.printHeading("Resolved Exception");
printResolvedException(result.getResolvedException());
this.printer.printHeading("ModelAndView");
printModelAndView(result.getModelAndView());
this.printer.printHeading("FlashMap");
printFlashMap(RequestContextUtils.getOutputFlashMap(result.getRequest()));
this.printer.printHeading("MockHttpServletResponse");
printResponse(result.getResponse());
}
/** Print the request */
protected void printRequest(MockHttpServletRequest request) throws Exception {
this.printer.printValue("HTTP Method", request.getMethod());
this.printer.printValue("Request URI", request.getRequestURI());
this.printer.printValue("Parameters", getParamsMultiValueMap(request));
this.printer.printValue("Headers", getRequestHeaders(request));
}
protected final HttpHeaders getRequestHeaders(MockHttpServletRequest request) {
HttpHeaders headers = new HttpHeaders();
Enumeration<?> names = request.getHeaderNames();
while (names.hasMoreElements()) {
String name = (String) names.nextElement();
Enumeration<String> values = request.getHeaders(name);
while (values.hasMoreElements()) {
headers.add(name, values.nextElement());
}
}
return headers;
}
protected final MultiValueMap<String, String> getParamsMultiValueMap(MockHttpServletRequest request) {
Map<String, String[]> params = request.getParameterMap();
MultiValueMap<String, String> multiValueMap = new LinkedMultiValueMap<String, String>();
for (String name : params.keySet()) {
if (params.get(name) != null) {
for (String value : params.get(name)) {
multiValueMap.add(name, value);
}
}
}
return multiValueMap;
}
protected void printAsyncResult(MvcResult result) throws Exception {
if (servlet3Present) {
HttpServletRequest request = result.getRequest();
this.printer.printValue("Was async started", request.isAsyncStarted());
this.printer.printValue("Async result", result.getAsyncResult(0));
}
}
/** Print the handler */
protected void printHandler(Object handler, HandlerInterceptor[] interceptors) throws Exception {
if (handler == null) {
this.printer.printValue("Type", null);
}
else {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
this.printer.printValue("Type", handlerMethod.getBeanType().getName());
this.printer.printValue("Method", handlerMethod);
}
else {
this.printer.printValue("Type", handler.getClass().getName());
}
}
}
/** Print exceptions resolved through a HandlerExceptionResolver */
protected void printResolvedException(Exception resolvedException) throws Exception {
if (resolvedException == null) {
this.printer.printValue("Type", null);
}
else {
this.printer.printValue("Type", resolvedException.getClass().getName());
}
}
/** Print the ModelAndView */
protected void printModelAndView(ModelAndView mav) throws Exception {
this.printer.printValue("View name", (mav != null) ? mav.getViewName() : null);
this.printer.printValue("View", (mav != null) ? mav.getView() : null);
if (mav == null || mav.getModel().size() == 0) {
this.printer.printValue("Model", null);
}
else {
for (String name : mav.getModel().keySet()) {
if (!name.startsWith(BindingResult.MODEL_KEY_PREFIX)) {
Object value = mav.getModel().get(name);
this.printer.printValue("Attribute", name);
this.printer.printValue("value", value);
Errors errors = (Errors) mav.getModel().get(BindingResult.MODEL_KEY_PREFIX + name);
if (errors != null) {
this.printer.printValue("errors", errors.getAllErrors());
}
}
}
}
}
/** Print "output" flash attributes */
protected void printFlashMap(FlashMap flashMap) throws Exception {
if (flashMap == null) {
this.printer.printValue("Attributes", null);
}
else {
for (String name : flashMap.keySet()) {
this.printer.printValue("Attribute", name);
this.printer.printValue("value", flashMap.get(name));
}
}
}
/** Print the response */
protected void printResponse(MockHttpServletResponse response) throws Exception {
this.printer.printValue("Status", response.getStatus());
this.printer.printValue("Error message", response.getErrorMessage());
this.printer.printValue("Headers", getResponseHeaders(response));
this.printer.printValue("Content type", response.getContentType());
this.printer.printValue("Body", response.getContentAsString());
this.printer.printValue("Forwarded URL", response.getForwardedUrl());
this.printer.printValue("Redirected URL", response.getRedirectedUrl());
this.printer.printValue("Cookies", response.getCookies());
}
protected final HttpHeaders getResponseHeaders(MockHttpServletResponse response) {
HttpHeaders headers = new HttpHeaders();
for (String name : response.getHeaderNames()) {
headers.put(name, response.getHeaders(name));
}
return headers;
}
/**
* A contract for how to actually write result information.
*/
protected interface ResultValuePrinter {
void printHeading(String heading);
void printValue(String label, Object value);
}
}

View File

@@ -0,0 +1,168 @@
/*
* Copyright 2002-2012 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.servlet.result;
import static org.springframework.test.util.AssertionErrors.assertEquals;
import static org.springframework.test.util.MatcherAssertionErrors.assertThat;
import java.util.concurrent.Callable;
import javax.servlet.http.HttpServletRequest;
import org.hamcrest.Matcher;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultMatcher;
import org.springframework.web.context.request.async.DeferredResult;
import org.springframework.web.context.request.async.WebAsyncTask;
/**
* Factory for assertions on the request. An instance of this class is
* typically accessed via {@link MockMvcResultMatchers#request()}.
*
* @author Rossen Stoyanchev
* @since 3.2
*/
public class RequestResultMatchers {
/**
* Protected constructor.
* Use {@link MockMvcResultMatchers#request()}.
*/
protected RequestResultMatchers() {
}
/**
* Assert a request attribute value with the given Hamcrest {@link Matcher}.
* Whether asynchronous processing started, usually as a result of a
* controller method returning {@link Callable} or {@link DeferredResult}.
* The test will await the completion of a {@code Callable} so that
* {@link #asyncResult(Matcher)} can be used to assert the resulting value.
* Neither a {@code Callable} nor a {@code DeferredResult} will complete
* processing all the way since a {@link MockHttpServletRequest} does not
* perform asynchronous dispatches.
*/
public ResultMatcher asyncStarted() {
return new ResultMatcher() {
@Override
public void match(MvcResult result) {
HttpServletRequest request = result.getRequest();
assertEquals("Async started", true, request.isAsyncStarted());
}
};
}
/**
* Assert that asynchronous processing was not start.
* @see #asyncStarted()
*/
public ResultMatcher asyncNotStarted() {
return new ResultMatcher() {
@Override
public void match(MvcResult result) {
HttpServletRequest request = result.getRequest();
assertEquals("Async started", false, request.isAsyncStarted());
}
};
}
/**
* Assert the result from asynchronous processing with the given matcher.
*/
public <T> ResultMatcher asyncResult(final Matcher<T> matcher) {
return new ResultMatcher() {
@Override
@SuppressWarnings("unchecked")
public void match(MvcResult result) {
HttpServletRequest request = result.getRequest();
assertEquals("Async started", true, request.isAsyncStarted());
assertThat("Async result", (T) result.getAsyncResult(), matcher);
}
};
}
/**
* Assert the result from asynchronous processing.
* This method can be used when a controller method returns {@link Callable}
* or {@link WebAsyncTask}. The value matched is the value returned from the
* {@code Callable} or the exception raised.
*/
public <T> ResultMatcher asyncResult(final Object expectedResult) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) {
HttpServletRequest request = result.getRequest();
assertEquals("Async started", true, request.isAsyncStarted());
assertEquals("Async result", expectedResult, result.getAsyncResult());
}
};
}
/**
* Assert a request attribute value with the given Hamcrest {@link Matcher}.
*/
public <T> ResultMatcher attribute(final String name, final Matcher<T> matcher) {
return new ResultMatcher() {
@Override
@SuppressWarnings("unchecked")
public void match(MvcResult result) {
T value = (T) result.getRequest().getAttribute(name);
assertThat("Request attribute", value, matcher);
}
};
}
/**
* Assert a request attribute value.
*/
public <T> ResultMatcher attribute(final String name, final Object expectedValue) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) {
assertEquals("Request attribute", expectedValue, result.getRequest().getAttribute(name));
}
};
}
/**
* Assert a session attribute value with the given Hamcrest {@link Matcher}.
*/
public <T> ResultMatcher sessionAttribute(final String name, final Matcher<T> matcher) {
return new ResultMatcher() {
@Override
@SuppressWarnings("unchecked")
public void match(MvcResult result) {
T value = (T) result.getRequest().getSession().getAttribute(name);
assertThat("Request attribute", value, matcher);
}
};
}
/**
* Assert a session attribute value..
*/
public <T> ResultMatcher sessionAttribute(final String name, final Object value) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) {
assertEquals("Request attribute", value, result.getRequest().getSession().getAttribute(name));
}
};
}
}

View File

@@ -0,0 +1,559 @@
/*
* Copyright 2002-2012 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.servlet.result;
import static org.springframework.test.util.AssertionErrors.assertEquals;
import static org.springframework.test.util.MatcherAssertionErrors.assertThat;
import org.hamcrest.Matcher;
import org.springframework.http.HttpStatus;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultMatcher;
/**
* Factory for assertions on the response status. An instance of this class is
* typically accessed via {@link MockMvcResultMatchers#status()}.
*
* @author Keesun Baik
* @author Rossen Stoyanchev
* @since 3.2
*/
public class StatusResultMatchers {
/**
* Protected constructor.
* Use {@link MockMvcResultMatchers#status()}.
*/
protected StatusResultMatchers() {
}
/**
* Assert the response status code with the given Hamcrest {@link Matcher}.
*/
public ResultMatcher is(final Matcher<Integer> matcher) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
assertThat("Response status", result.getResponse().getStatus(), matcher);
}
};
}
/**
* Assert the response status code is equal to an integer value.
*/
public ResultMatcher is(final int status) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
assertEquals("Response status", status, result.getResponse().getStatus());
}
};
}
/**
* Assert the Servlet response error message with the given Hamcrest {@link Matcher}.
*/
public ResultMatcher reason(final Matcher<? super String> matcher) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
assertThat("Response status reason", result.getResponse().getErrorMessage(), matcher);
}
};
}
/**
* Assert the Servlet response error message.
*/
public ResultMatcher reason(final String reason) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
assertEquals("Response status reason", reason, result.getResponse().getErrorMessage());
}
};
}
/**
* Assert the response status code is {@code HttpStatus.CONTINUE} (100).
*/
public ResultMatcher isContinue() {
return matcher(HttpStatus.CONTINUE);
}
/**
* Assert the response status code is {@code HttpStatus.SWITCHING_PROTOCOLS} (101).
*/
public ResultMatcher isSwitchingProtocols() {
return matcher(HttpStatus.SWITCHING_PROTOCOLS);
}
/**
* Assert the response status code is {@code HttpStatus.PROCESSING} (102).
*/
public ResultMatcher isProcessing() {
return matcher(HttpStatus.PROCESSING);
}
/**
* Assert the response status code is {@code HttpStatus.CHECKPOINT} (103).
*/
public ResultMatcher isCheckpoint() {
return matcher(HttpStatus.valueOf(103));
}
/**
* Assert the response status code is {@code HttpStatus.OK} (200).
*/
public ResultMatcher isOk() {
return matcher(HttpStatus.OK);
}
/**
* Assert the response status code is {@code HttpStatus.CREATED} (201).
*/
public ResultMatcher isCreated() {
return matcher(HttpStatus.CREATED);
}
/**
* Assert the response status code is {@code HttpStatus.ACCEPTED} (202).
*/
public ResultMatcher isAccepted() {
return matcher(HttpStatus.ACCEPTED);
}
/**
* Assert the response status code is {@code HttpStatus.NON_AUTHORITATIVE_INFORMATION} (203).
*/
public ResultMatcher isNonAuthoritativeInformation() {
return matcher(HttpStatus.NON_AUTHORITATIVE_INFORMATION);
}
/**
* Assert the response status code is {@code HttpStatus.NO_CONTENT} (204).
*/
public ResultMatcher isNoContent() {
return matcher(HttpStatus.NO_CONTENT);
}
/**
* Assert the response status code is {@code HttpStatus.RESET_CONTENT} (205).
*/
public ResultMatcher isResetContent() {
return matcher(HttpStatus.RESET_CONTENT);
}
/**
* Assert the response status code is {@code HttpStatus.PARTIAL_CONTENT} (206).
*/
public ResultMatcher isPartialContent() {
return matcher(HttpStatus.PARTIAL_CONTENT);
}
/**
* Assert the response status code is {@code HttpStatus.MULTI_STATUS} (207).
*/
public ResultMatcher isMultiStatus() {
return matcher(HttpStatus.MULTI_STATUS);
}
/**
* Assert the response status code is {@code HttpStatus.ALREADY_REPORTED} (208).
*/
public ResultMatcher isAlreadyReported() {
return matcher(HttpStatus.ALREADY_REPORTED);
}
/**
* Assert the response status code is {@code HttpStatus.IM_USED} (226).
*/
public ResultMatcher isImUsed() {
return matcher(HttpStatus.IM_USED);
}
/**
* Assert the response status code is {@code HttpStatus.MULTIPLE_CHOICES} (300).
*/
public ResultMatcher isMultipleChoices() {
return matcher(HttpStatus.MULTIPLE_CHOICES);
}
/**
* Assert the response status code is {@code HttpStatus.MOVED_PERMANENTLY} (301).
*/
public ResultMatcher isMovedPermanently() {
return matcher(HttpStatus.MOVED_PERMANENTLY);
}
/**
* Assert the response status code is {@code HttpStatus.FOUND} (302).
*/
public ResultMatcher isFound() {
return matcher(HttpStatus.FOUND);
}
/**
* Assert the response status code is {@code HttpStatus.MOVED_TEMPORARILY} (302).
*/
public ResultMatcher isMovedTemporarily() {
return matcher(HttpStatus.MOVED_TEMPORARILY);
}
/**
* Assert the response status code is {@code HttpStatus.SEE_OTHER} (303).
*/
public ResultMatcher isSeeOther() {
return matcher(HttpStatus.SEE_OTHER);
}
/**
* Assert the response status code is {@code HttpStatus.NOT_MODIFIED} (304).
*/
public ResultMatcher isNotModified() {
return matcher(HttpStatus.NOT_MODIFIED);
}
/**
* Assert the response status code is {@code HttpStatus.USE_PROXY} (305).
*/
public ResultMatcher isUseProxy() {
return matcher(HttpStatus.USE_PROXY);
}
/**
* Assert the response status code is {@code HttpStatus.TEMPORARY_REDIRECT} (307).
*/
public ResultMatcher isTemporaryRedirect() {
return matcher(HttpStatus.TEMPORARY_REDIRECT);
}
/**
* Assert the response status code is {@code HttpStatus.RESUME_INCOMPLETE} (308).
*/
public ResultMatcher isResumeIncomplete() {
return matcher(HttpStatus.valueOf(308));
}
/**
* Assert the response status code is {@code HttpStatus.BAD_REQUEST} (400).
*/
public ResultMatcher isBadRequest() {
return matcher(HttpStatus.BAD_REQUEST);
}
/**
* Assert the response status code is {@code HttpStatus.UNAUTHORIZED} (401).
*/
public ResultMatcher isUnauthorized() {
return matcher(HttpStatus.UNAUTHORIZED);
}
/**
* Assert the response status code is {@code HttpStatus.PAYMENT_REQUIRED} (402).
*/
public ResultMatcher isPaymentRequired() {
return matcher(HttpStatus.PAYMENT_REQUIRED);
}
/**
* Assert the response status code is {@code HttpStatus.FORBIDDEN} (403).
*/
public ResultMatcher isForbidden() {
return matcher(HttpStatus.FORBIDDEN);
}
/**
* Assert the response status code is {@code HttpStatus.NOT_FOUND} (404).
*/
public ResultMatcher isNotFound() {
return matcher(HttpStatus.NOT_FOUND);
}
/**
* Assert the response status code is {@code HttpStatus.METHOD_NOT_ALLOWED} (405).
*/
public ResultMatcher isMethodNotAllowed() {
return matcher(HttpStatus.METHOD_NOT_ALLOWED);
}
/**
* Assert the response status code is {@code HttpStatus.NOT_ACCEPTABLE} (406).
*/
public ResultMatcher isNotAcceptable() {
return matcher(HttpStatus.NOT_ACCEPTABLE);
}
/**
* Assert the response status code is {@code HttpStatus.PROXY_AUTHENTICATION_REQUIRED} (407).
*/
public ResultMatcher isProxyAuthenticationRequired() {
return matcher(HttpStatus.PROXY_AUTHENTICATION_REQUIRED);
}
/**
* Assert the response status code is {@code HttpStatus.REQUEST_TIMEOUT} (408).
*/
public ResultMatcher isRequestTimeout() {
return matcher(HttpStatus.REQUEST_TIMEOUT);
}
/**
* Assert the response status code is {@code HttpStatus.CONFLICT} (409).
*/
public ResultMatcher isConflict() {
return matcher(HttpStatus.CONFLICT);
}
/**
* Assert the response status code is {@code HttpStatus.GONE} (410).
*/
public ResultMatcher isGone() {
return matcher(HttpStatus.GONE);
}
/**
* Assert the response status code is {@code HttpStatus.LENGTH_REQUIRED} (411).
*/
public ResultMatcher isLengthRequired() {
return matcher(HttpStatus.LENGTH_REQUIRED);
}
/**
* Assert the response status code is {@code HttpStatus.PRECONDITION_FAILED} (412).
*/
public ResultMatcher isPreconditionFailed() {
return matcher(HttpStatus.PRECONDITION_FAILED);
}
/**
* Assert the response status code is {@code HttpStatus.REQUEST_ENTITY_TOO_LARGE} (413).
*/
public ResultMatcher isRequestEntityTooLarge() {
return matcher(HttpStatus.REQUEST_ENTITY_TOO_LARGE);
}
/**
* Assert the response status code is {@code HttpStatus.REQUEST_URI_TOO_LONG} (414).
*/
public ResultMatcher isRequestUriTooLong() {
return matcher(HttpStatus.REQUEST_URI_TOO_LONG);
}
/**
* Assert the response status code is {@code HttpStatus.UNSUPPORTED_MEDIA_TYPE} (415).
*/
public ResultMatcher isUnsupportedMediaType() {
return matcher(HttpStatus.UNSUPPORTED_MEDIA_TYPE);
}
/**
* Assert the response status code is {@code HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE} (416).
*/
public ResultMatcher isRequestedRangeNotSatisfiable() {
return matcher(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE);
}
/**
* Assert the response status code is {@code HttpStatus.EXPECTATION_FAILED} (417).
*/
public ResultMatcher isExpectationFailed() {
return matcher(HttpStatus.EXPECTATION_FAILED);
}
/**
* Assert the response status code is {@code HttpStatus.I_AM_A_TEAPOT} (418).
*/
public ResultMatcher isIAmATeapot() {
return matcher(HttpStatus.valueOf(418));
}
/**
* Assert the response status code is {@code HttpStatus.INSUFFICIENT_SPACE_ON_RESOURCE} (419).
* @deprecated matching the deprecation of HttpStatus.INSUFFICIENT_SPACE_ON_RESOURCE
*/
@Deprecated
public ResultMatcher isInsufficientSpaceOnResource() {
return matcher(HttpStatus.INSUFFICIENT_SPACE_ON_RESOURCE);
}
/**
* Assert the response status code is {@code HttpStatus.METHOD_FAILURE} (420).
* @deprecated matching the deprecation of HttpStatus.METHOD_FAILURE
*/
@Deprecated
public ResultMatcher isMethodFailure() {
return matcher(HttpStatus.METHOD_FAILURE);
}
/**
* Assert the response status code is {@code HttpStatus.DESTINATION_LOCKED} (421).
* @deprecated matching the deprecation of HttpStatus.DESTINATION_LOCKED
*/
@Deprecated
public ResultMatcher isDestinationLocked() {
return matcher(HttpStatus.DESTINATION_LOCKED);
}
/**
* Assert the response status code is {@code HttpStatus.UNPROCESSABLE_ENTITY} (422).
*/
public ResultMatcher isUnprocessableEntity() {
return matcher(HttpStatus.UNPROCESSABLE_ENTITY);
}
/**
* Assert the response status code is {@code HttpStatus.LOCKED} (423).
*/
public ResultMatcher isLocked() {
return matcher(HttpStatus.LOCKED);
}
/**
* Assert the response status code is {@code HttpStatus.FAILED_DEPENDENCY} (424).
*/
public ResultMatcher isFailedDependency() {
return matcher(HttpStatus.FAILED_DEPENDENCY);
}
/**
* Assert the response status code is {@code HttpStatus.UPGRADE_REQUIRED} (426).
*/
public ResultMatcher isUpgradeRequired() {
return matcher(HttpStatus.UPGRADE_REQUIRED);
}
/**
* Assert the response status code is {@code HttpStatus.PRECONDITION_REQUIRED} (428).
*/
public ResultMatcher isPreconditionRequired() {
return matcher(HttpStatus.valueOf(428));
}
/**
* Assert the response status code is {@code HttpStatus.TOO_MANY_REQUESTS} (429).
*/
public ResultMatcher isTooManyRequests() {
return matcher(HttpStatus.valueOf(429));
}
/**
* Assert the response status code is {@code HttpStatus.REQUEST_HEADER_FIELDS_TOO_LARGE} (431).
*/
public ResultMatcher isRequestHeaderFieldsTooLarge() {
return matcher(HttpStatus.valueOf(431));
}
/**
* Assert the response status code is {@code HttpStatus.INTERNAL_SERVER_ERROR} (500).
*/
public ResultMatcher isInternalServerError() {
return matcher(HttpStatus.INTERNAL_SERVER_ERROR);
}
/**
* Assert the response status code is {@code HttpStatus.NOT_IMPLEMENTED} (501).
*/
public ResultMatcher isNotImplemented() {
return matcher(HttpStatus.NOT_IMPLEMENTED);
}
/**
* Assert the response status code is {@code HttpStatus.BAD_GATEWAY} (502).
*/
public ResultMatcher isBadGateway() {
return matcher(HttpStatus.BAD_GATEWAY);
}
/**
* Assert the response status code is {@code HttpStatus.SERVICE_UNAVAILABLE} (503).
*/
public ResultMatcher isServiceUnavailable() {
return matcher(HttpStatus.SERVICE_UNAVAILABLE);
}
/**
* Assert the response status code is {@code HttpStatus.GATEWAY_TIMEOUT} (504).
*/
public ResultMatcher isGatewayTimeout() {
return matcher(HttpStatus.GATEWAY_TIMEOUT);
}
/**
* Assert the response status code is {@code HttpStatus.HTTP_VERSION_NOT_SUPPORTED} (505).
*/
public ResultMatcher isHttpVersionNotSupported() {
return matcher(HttpStatus.HTTP_VERSION_NOT_SUPPORTED);
}
/**
* Assert the response status code is {@code HttpStatus.VARIANT_ALSO_NEGOTIATES} (506).
*/
public ResultMatcher isVariantAlsoNegotiates() {
return matcher(HttpStatus.VARIANT_ALSO_NEGOTIATES);
}
/**
* Assert the response status code is {@code HttpStatus.INSUFFICIENT_STORAGE} (507).
*/
public ResultMatcher isInsufficientStorage() {
return matcher(HttpStatus.INSUFFICIENT_STORAGE);
}
/**
* Assert the response status code is {@code HttpStatus.LOOP_DETECTED} (508).
*/
public ResultMatcher isLoopDetected() {
return matcher(HttpStatus.LOOP_DETECTED);
}
/**
* Assert the response status code is {@code HttpStatus.BANDWIDTH_LIMIT_EXCEEDED} (509).
*/
public ResultMatcher isBandwidthLimitExceeded() {
return matcher(HttpStatus.valueOf(509));
}
/**
* Assert the response status code is {@code HttpStatus.NOT_EXTENDED} (510).
*/
public ResultMatcher isNotExtended() {
return matcher(HttpStatus.NOT_EXTENDED);
}
/**
* Assert the response status code is {@code HttpStatus.NETWORK_AUTHENTICATION_REQUIRED} (511).
*/
public ResultMatcher isNetworkAuthenticationRequired() {
return matcher(HttpStatus.valueOf(511));
}
/**
* Match the expected response status to that of the HttpServletResponse
*/
private ResultMatcher matcher(final HttpStatus status) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) {
assertEquals("Status", status.value(), result.getResponse().getStatus());
}
};
}
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright 2002-2012 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.servlet.result;
import static org.springframework.test.util.MatcherAssertionErrors.assertThat;
import static org.springframework.test.util.AssertionErrors.*;
import org.hamcrest.Matcher;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultMatcher;
import org.springframework.web.servlet.ModelAndView;
/**
* Factory for assertions on the selected view. An instance of this class is
* typically accessed via {@link MockMvcResultMatchers#view()}.
* @since 3.2
*/
public class ViewResultMatchers {
/**
* Protected constructor.
* Use {@link MockMvcResultMatchers#view()}.
*/
protected ViewResultMatchers() {
}
/**
* Assert the selected view name with the given Hamcrest {@link Matcher}.
*/
public ResultMatcher name(final Matcher<? super String> matcher) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
ModelAndView mav = result.getModelAndView();
assertTrue("No ModelAndView found", mav != null);
assertThat("View name", mav.getViewName(), matcher);
}
};
}
/**
* Assert the selected view name.
*/
public ResultMatcher name(final String expectedViewName) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
ModelAndView mav = result.getModelAndView();
assertTrue("No ModelAndView found", mav != null);
assertEquals("View name", expectedViewName, mav.getViewName());
}
};
}
}

View File

@@ -0,0 +1,194 @@
/*
* Copyright 2002-2012 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.servlet.result;
import java.util.Map;
import javax.xml.xpath.XPathExpressionException;
import org.hamcrest.Matcher;
import org.springframework.test.util.XpathExpectationsHelper;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultMatcher;
import org.w3c.dom.Node;
/**
* Factory for response content {@code ResultMatcher}'s using an XPath
* expression. An instance of this class is typically accessed via
* {@link MockMvcResultMatchers#xpath}.
*
* @author Rossen Stoyanchev
* @since 3.2
*/
public class XpathResultMatchers {
private final XpathExpectationsHelper xpathHelper;
/**
* Protected constructor, not for direct instantiation. Use
* {@link MockMvcResultMatchers#xpath(String, Object...)} or
* {@link MockMvcResultMatchers#xpath(String, Map, Object...)}.
*
* @param expression the XPath expression
* @param namespaces XML namespaces referenced in the XPath expression, or {@code null}
* @param args arguments to parameterize the XPath expression with using the
* formatting specifiers defined in {@link String#format(String, Object...)}
*
* @throws XPathExpressionException
*/
protected XpathResultMatchers(String expression, Map<String, String> namespaces, Object ... args)
throws XPathExpressionException {
this.xpathHelper = new XpathExpectationsHelper(expression, namespaces, args);
}
/**
* Evaluate the XPath and assert the {@link Node} content found with the
* given Hamcrest {@link Matcher}.
*/
public ResultMatcher node(final Matcher<? super Node> matcher) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
String content = result.getResponse().getContentAsString();
xpathHelper.assertNode(content, matcher);
}
};
}
/**
* Evaluate the XPath and assert that content exists.
*/
public ResultMatcher exists() {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
String content = result.getResponse().getContentAsString();
xpathHelper.exists(content);
}
};
}
/**
* Evaluate the XPath and assert that content doesn't exist.
*/
public ResultMatcher doesNotExist() {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
String content = result.getResponse().getContentAsString();
xpathHelper.doesNotExist(content);
}
};
}
/**
* Evaluate the XPath and assert the number of nodes found with the given
* Hamcrest {@link Matcher}.
*/
public ResultMatcher nodeCount(final Matcher<Integer> matcher) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
String content = result.getResponse().getContentAsString();
xpathHelper.assertNodeCount(content, matcher);
}
};
}
/**
* Evaluate the XPath and assert the number of nodes found.
*/
public ResultMatcher nodeCount(final int expectedCount) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
String content = result.getResponse().getContentAsString();
xpathHelper.assertNodeCount(content, expectedCount);
}
};
}
/**
* Apply the XPath and assert the {@link String} value found with the given
* Hamcrest {@link Matcher}.
*/
public ResultMatcher string(final Matcher<? super String> matcher) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
String content = result.getResponse().getContentAsString();
xpathHelper.assertString(content, matcher);
}
};
}
/**
* Apply the XPath and assert the {@link String} value found.
*/
public ResultMatcher string(final String expectedValue) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
String content = result.getResponse().getContentAsString();
xpathHelper.assertString(content, expectedValue);
}
};
}
/**
* Evaluate the XPath and assert the {@link Double} value found with the
* given Hamcrest {@link Matcher}.
*/
public ResultMatcher number(final Matcher<? super Double> matcher) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
String content = result.getResponse().getContentAsString();
xpathHelper.assertNumber(content, matcher);
}
};
}
/**
* Evaluate the XPath and assert the {@link Double} value found.
*/
public ResultMatcher number(final Double expectedValue) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
String content = result.getResponse().getContentAsString();
xpathHelper.assertNumber(content, expectedValue);
}
};
}
/**
* Evaluate the XPath and assert the {@link Boolean} value found.
*/
public ResultMatcher booleanValue(final Boolean value) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
String content = result.getResponse().getContentAsString();
xpathHelper.assertBoolean(content, value);
}
};
}
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright 2002-2012 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.
*/
/**
* Contains built-in {@code ResultMatcher} and {@code ResultHandler} implementations.
* Use {@link org.springframework.test.web.servlet.result.MockMvcResultMatchers}
* and {@link org.springframework.test.web.servlet.result.MockMvcResultHandlers}
* to access to instances of those implementations.
*/
package org.springframework.test.web.servlet.result;

View File

@@ -0,0 +1,220 @@
/*
* Copyright 2002-2013 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.servlet.setup;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.Filter;
import javax.servlet.ServletContext;
import org.springframework.mock.web.MockServletConfig;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MockMvcBuilder;
import org.springframework.test.web.servlet.MockMvcBuilderSupport;
import org.springframework.test.web.servlet.RequestBuilder;
import org.springframework.test.web.servlet.ResultHandler;
import org.springframework.test.web.servlet.ResultMatcher;
import org.springframework.util.Assert;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
/**
* An concrete implementation of {@link MockMvcBuilder} with methods for
* configuring filters, default request properties, and global expectations and
* result actions.
*
* @author Rossen Stoyanchev
* @author Rob Winch
* @since 3.2
*/
public class DefaultMockMvcBuilder<Self extends DefaultMockMvcBuilder> extends MockMvcBuilderSupport
implements MockMvcBuilder {
private final WebApplicationContext webAppContext;
private List<Filter> filters = new ArrayList<Filter>();
private RequestBuilder defaultRequestBuilder;
private final List<ResultMatcher> globalResultMatchers = new ArrayList<ResultMatcher>();
private final List<ResultHandler> globalResultHandlers = new ArrayList<ResultHandler>();
private Boolean dispatchOptions = Boolean.FALSE;
/**
* Protected constructor. Not intended for direct instantiation.
* @see MockMvcBuilders#webAppContextSetup(WebApplicationContext)
*/
protected DefaultMockMvcBuilder(WebApplicationContext webAppContext) {
Assert.notNull(webAppContext, "WebApplicationContext is required");
Assert.notNull(webAppContext.getServletContext(), "WebApplicationContext must have a ServletContext");
this.webAppContext = webAppContext;
}
/**
* Add filters mapped to any request (i.e. "/*"). For example:
*
* <pre class="code">
* mockMvcBuilder.addFilters(springSecurityFilterChain);
* </pre>
*
* <p>is the equivalent of the following web.xml configuration:
*
* <pre class="code">
* &lt;filter-mapping&gt;
* &lt;filter-name&gt;springSecurityFilterChain&lt;/filter-name&gt;
* &lt;url-pattern&gt;/*&lt;/url-pattern&gt;
* &lt;/filter-mapping&gt;
* </pre>
*
* <p>Filters will be invoked in the order in which they are provided.
*
* @param filters the filters to add
*/
@SuppressWarnings("unchecked")
public final <T extends Self> T addFilters(Filter... filters) {
Assert.notNull(filters, "filters cannot be null");
for(Filter f : filters) {
Assert.notNull(f, "filters cannot contain null values");
this.filters.add(f);
}
return (T) this;
}
/**
* Add a filter mapped to a specific set of patterns. For example:
*
* <pre class="code">
* mockMvcBuilder.addFilters(myResourceFilter, "/resources/*");
* </pre>
*
* <p>is the equivalent of:
*
* <pre class="code">
* &lt;filter-mapping&gt;
* &lt;filter-name&gt;myResourceFilter&lt;/filter-name&gt;
* &lt;url-pattern&gt;/resources/*&lt;/url-pattern&gt;
* &lt;/filter-mapping&gt;
* </pre>
*
* <p>Filters will be invoked in the order in which they are provided.
*
* @param filter the filter to add
* @param urlPatterns URL patterns to map to; if empty, "/*" is used by default
*/
@SuppressWarnings("unchecked")
public final <T extends Self> T addFilter(Filter filter, String... urlPatterns) {
Assert.notNull(filter, "filter cannot be null");
Assert.notNull(urlPatterns, "urlPatterns cannot be null");
if(urlPatterns.length > 0) {
filter = new PatternMappingFilterProxy(filter, urlPatterns);
}
this.filters.add(filter);
return (T) this;
}
/**
* Define default request properties that should be merged into all
* performed requests. In effect this provides a mechanism for defining
* common initialization for all requests such as the content type, request
* parameters, session attributes, and any other request property.
*
* <p>Properties specified at the time of performing a request override the
* default properties defined here.
*
* @param requestBuilder a RequestBuilder; see static factory methods in
* {@link org.springframework.test.web.servlet.request.MockMvcRequestBuilders}
* .
*/
@SuppressWarnings("unchecked")
public final <T extends Self> T defaultRequest(RequestBuilder requestBuilder) {
this.defaultRequestBuilder = requestBuilder;
return (T) this;
}
/**
* Define a global expectation that should <em>always</em> be applied to
* every response. For example, status code 200 (OK), content type
* {@code "application/json"}, etc.
*
* @param resultMatcher a ResultMatcher; see static factory methods in
* {@link org.springframework.test.web.servlet.result.MockMvcResultMatchers}
*/
@SuppressWarnings("unchecked")
public final <T extends Self> T alwaysExpect(ResultMatcher resultMatcher) {
this.globalResultMatchers.add(resultMatcher);
return (T) this;
}
/**
* Define a global action that should <em>always</em> be applied to every
* response. For example, writing detailed information about the performed
* request and resulting response to {@code System.out}.
*
* @param resultHandler a ResultHandler; see static factory methods in
* {@link org.springframework.test.web.servlet.result.MockMvcResultHandlers}
*/
@SuppressWarnings("unchecked")
public final <T extends Self> T alwaysDo(ResultHandler resultHandler) {
this.globalResultHandlers.add(resultHandler);
return (T) this;
}
/**
* Should the {@link DispatcherServlet} dispatch OPTIONS request to controllers.
* @param dispatchOptions
* @see DispatcherServlet#setDispatchOptionsRequest(boolean)
*/
@SuppressWarnings("unchecked")
public final <T extends Self> T dispatchOptions(boolean dispatchOptions) {
this.dispatchOptions = dispatchOptions;
return (T) this;
}
/**
* Build a {@link MockMvc} instance.
*/
@Override
public final MockMvc build() {
initWebAppContext(this.webAppContext);
ServletContext servletContext = this.webAppContext.getServletContext();
MockServletConfig mockServletConfig = new MockServletConfig(servletContext);
Filter[] filterArray = this.filters.toArray(new Filter[this.filters.size()]);
return super.createMockMvc(filterArray, mockServletConfig, this.webAppContext,
this.defaultRequestBuilder, this.globalResultMatchers, this.globalResultHandlers,this.dispatchOptions);
}
/**
* Invoked from {@link #build()} before the {@link MockMvc} instance is created.
* Allows sub-classes to further initialize the {@code WebApplicationContext}
* and the {@code javax.servlet.ServletContext} it contains.
*/
protected void initWebAppContext(WebApplicationContext webAppContext) {
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright 2002-2012 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.servlet.setup;
import javax.servlet.ServletContext;
import org.springframework.stereotype.Controller;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MockMvcBuilder;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
/**
* The main class to import to access all available {@link MockMvcBuilder}s.
*
* <p><strong>Eclipse users:</strong> consider adding this class as a Java editor
* favorite. To navigate, open the Preferences and type "favorites".
*
* @author Rossen Stoyanchev
* @since 3.2
*/
public class MockMvcBuilders {
/**
* Build a {@link MockMvc} using the given, fully initialized, i.e.
* refreshed, {@link WebApplicationContext}. The {@link DispatcherServlet}
* will use the context to discover Spring MVC infrastructure and
* application controllers in it. The context must have been configured with
* a {@link ServletContext}.
*/
public static DefaultMockMvcBuilder<DefaultMockMvcBuilder<?>> webAppContextSetup(WebApplicationContext context) {
return new DefaultMockMvcBuilder<DefaultMockMvcBuilder<?>>(context);
}
/**
* Build a {@link MockMvc} by registering one or more {@code @Controller}'s
* instances and configuring Spring MVC infrastructure programmatically.
* This allows full control over the instantiation and initialization of
* controllers, and their dependencies, similar to plain unit tests while
* also making it possible to test one controller at a time.
*
* <p>When this option is used, the minimum infrastructure required by the
* {@link DispatcherServlet} to serve requests with annotated controllers is
* automatically created, and can be customized, resulting in configuration
* that is equivalent to what the MVC Java configuration provides except
* using builder style methods.
*
* <p>If the Spring MVC configuration of an application is relatively
* straight-forward, for example when using the MVC namespace or the MVC
* Java config, then using this builder might be a good option for testing
* a majority of controllers. A much smaller number of tests can be used
* to focus on testing and verifying the actual Spring MVC configuration.
*
* @param controllers one or more {@link Controller @Controller}'s to test
*/
public static StandaloneMockMvcBuilder standaloneSetup(Object... controllers) {
return new StandaloneMockMvcBuilder(controllers);
}
}

View File

@@ -0,0 +1,133 @@
/*
* Copyright 2002-2012 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.servlet.setup;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.springframework.util.Assert;
import org.springframework.web.util.UrlPathHelper;
/**
* A Filter that invokes a delegate {@link Filter} only if the request URL
* matches the pattern it is mapped to using pattern matching as defined in the
* Servlet spec.
*
* @author Rob Winch
* @since 3.2
*/
final class PatternMappingFilterProxy implements Filter {
private static final String EXTENSION_MAPPING_PATTERN = "*.";
private static final String PATH_MAPPING_PATTERN = "/*";
private static final UrlPathHelper urlPathHelper = new UrlPathHelper();
private final Filter delegate;
/** Patterns that require an exact match, e.g. "/test" */
private final List<String> exactMatches = new ArrayList<String>();
/** Patterns that require the URL to have a specific prefix, e.g. "/test/*" */
private final List<String> startsWithMatches = new ArrayList<String>();
/** Patterns that require the request URL to have a specific suffix, e.g. "*.html" */
private final List<String> endsWithMatches = new ArrayList<String>();
/**
* Creates a new instance.
*/
public PatternMappingFilterProxy(Filter delegate, String... urlPatterns) {
Assert.notNull(delegate, "A delegate Filter is required");
this.delegate = delegate;
for(String urlPattern : urlPatterns) {
addUrlPattern(urlPattern);
}
}
private void addUrlPattern(String urlPattern) {
Assert.notNull(urlPattern, "Found null URL Pattern");
if(urlPattern.startsWith(EXTENSION_MAPPING_PATTERN)) {
this.endsWithMatches.add(urlPattern.substring(1, urlPattern.length()));
} else if(urlPattern.equals(PATH_MAPPING_PATTERN)) {
this.startsWithMatches.add("");
}
else if (urlPattern.endsWith(PATH_MAPPING_PATTERN)) {
this.startsWithMatches.add(urlPattern.substring(0, urlPattern.length() - 1));
this.exactMatches.add(urlPattern.substring(0, urlPattern.length() - 2));
} else {
if("".equals(urlPattern)) {
urlPattern = "/";
}
this.exactMatches.add(urlPattern);
}
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String requestPath = urlPathHelper.getPathWithinApplication(httpRequest);
if(matches(requestPath)) {
this.delegate.doFilter(request, response, filterChain);
} else {
filterChain.doFilter(request, response);
}
}
private boolean matches(String requestPath) {
for(String pattern : this.exactMatches) {
if(pattern.equals(requestPath)) {
return true;
}
}
if(!requestPath.startsWith("/")) {
return false;
}
for(String pattern : this.endsWithMatches) {
if(requestPath.endsWith(pattern)) {
return true;
}
}
for(String pattern : this.startsWithMatches) {
if(requestPath.startsWith(pattern)) {
return true;
}
}
return false;
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
this.delegate.init(filterConfig);
}
@Override
public void destroy() {
this.delegate.destroy();
}
}

View File

@@ -0,0 +1,488 @@
/*
* Copyright 2002-2013 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.servlet.setup;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.format.support.DefaultFormattingConversionService;
import org.springframework.format.support.FormattingConversionService;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.mock.web.MockServletContext;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.PropertyPlaceholderHelper;
import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver;
import org.springframework.util.StringValueResolver;
import org.springframework.validation.Validator;
import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationObjectSupport;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.FlashMapManager;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.handler.AbstractHandlerMapping;
import org.springframework.web.servlet.handler.MappedInterceptor;
import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.servlet.support.SessionFlashMapManager;
import org.springframework.web.servlet.theme.FixedThemeResolver;
import org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
/**
* A MockMvcBuilder that accepts {@code @Controller} registrations thus allowing
* full control over the instantiation and the initialization of controllers and
* their dependencies similar to plain unit tests, and also making it possible
* to test one controller at a time.
*
* <p>This builder creates the minimum infrastructure required by the
* {@link DispatcherServlet} to serve requests with annotated controllers and
* also provides methods to customize it. The resulting configuration and
* customizations possible are equivalent to using the MVC Java config except
* using builder style methods.
*
* <p>To configure view resolution, either select a "fixed" view to use for every
* performed request (see {@link #setSingleView(View)}) or provide a list of
* {@code ViewResolver}'s, see {@link #setViewResolvers(ViewResolver...)}.
*
* @author Rossen Stoyanchev
* @since 3.2
*/
public class StandaloneMockMvcBuilder extends DefaultMockMvcBuilder<StandaloneMockMvcBuilder> {
private final Object[] controllers;
private List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
private List<HandlerMethodArgumentResolver> customArgumentResolvers = new ArrayList<HandlerMethodArgumentResolver>();
private List<HandlerMethodReturnValueHandler> customReturnValueHandlers = new ArrayList<HandlerMethodReturnValueHandler>();
private final List<MappedInterceptor> mappedInterceptors = new ArrayList<MappedInterceptor>();
private Validator validator = null;
private ContentNegotiationManager contentNegotiationManager;
private FormattingConversionService conversionService = null;
private List<HandlerExceptionResolver> handlerExceptionResolvers = new ArrayList<HandlerExceptionResolver>();
private Long asyncRequestTimeout;
private List<ViewResolver> viewResolvers;
private LocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
private FlashMapManager flashMapManager = null;
private boolean useSuffixPatternMatch = true;
private boolean useTrailingSlashPatternMatch = true;
private Boolean removeSemicolonContent;
private Map<String, String> placeHolderValues = new HashMap<String, String>();
/**
* Protected constructor. Not intended for direct instantiation.
* @see MockMvcBuilders#standaloneSetup(Object...)
*/
protected StandaloneMockMvcBuilder(Object... controllers) {
super(new StubWebApplicationContext(new MockServletContext()));
Assert.isTrue(!ObjectUtils.isEmpty(controllers), "At least one controller is required");
this.controllers = controllers;
}
/**
* Set the message converters to use in argument resolvers and in return value
* handlers, which support reading and/or writing to the body of the request
* and response. If no message converters are added to the list, a default
* list of converters is added instead.
*/
public StandaloneMockMvcBuilder setMessageConverters(HttpMessageConverter<?>...messageConverters) {
this.messageConverters = Arrays.asList(messageConverters);
return this;
}
/**
* Provide a custom {@link Validator} instead of the one created by default.
* The default implementation used, assuming JSR-303 is on the classpath, is
* {@link org.springframework.validation.beanvalidation.LocalValidatorFactoryBean}.
*/
public StandaloneMockMvcBuilder setValidator(Validator validator) {
this.validator = validator;
return this;
}
/**
* Provide a conversion service with custom formatters and converters.
* If not set, a {@link DefaultFormattingConversionService} is used by default.
*/
public StandaloneMockMvcBuilder setConversionService(FormattingConversionService conversionService) {
this.conversionService = conversionService;
return this;
}
/**
* Add interceptors mapped to all incoming requests.
*/
public StandaloneMockMvcBuilder addInterceptors(HandlerInterceptor... interceptors) {
addMappedInterceptors(null, interceptors);
return this;
}
/**
* Add interceptors mapped to a set of path patterns.
*/
public StandaloneMockMvcBuilder addMappedInterceptors(String[] pathPatterns, HandlerInterceptor... interceptors) {
for (HandlerInterceptor interceptor : interceptors) {
this.mappedInterceptors.add(new MappedInterceptor(pathPatterns, interceptor));
}
return this;
}
/**
* Set a ContentNegotiationManager.
*/
protected StandaloneMockMvcBuilder setContentNegotiationManager(ContentNegotiationManager contentNegotiationManager) {
this.contentNegotiationManager = contentNegotiationManager;
return this;
}
/**
* Specify the timeout value for async execution. In Spring MVC Test, this
* value is used to determine how to long to wait for async execution to
* complete so that a test can verify the results synchronously.
* @param timeout the timeout value in milliseconds
*/
public StandaloneMockMvcBuilder setAsyncRequestTimeout(long timeout) {
this.asyncRequestTimeout = timeout;
return this;
}
/**
* Provide custom resolvers for controller method arguments.
*/
public StandaloneMockMvcBuilder setCustomArgumentResolvers(HandlerMethodArgumentResolver... argumentResolvers) {
this.customArgumentResolvers = Arrays.asList(argumentResolvers);
return this;
}
/**
* Provide custom handlers for controller method return values.
*/
public StandaloneMockMvcBuilder setCustomReturnValueHandlers(HandlerMethodReturnValueHandler... handlers) {
this.customReturnValueHandlers = Arrays.asList(handlers);
return this;
}
/**
* Set the HandlerExceptionResolver types to use as a list.
*/
public StandaloneMockMvcBuilder setHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
this.handlerExceptionResolvers = exceptionResolvers;
return this;
}
/**
* Set the HandlerExceptionResolver types to use as an array.
*/
public StandaloneMockMvcBuilder setHandlerExceptionResolvers(HandlerExceptionResolver... exceptionResolvers) {
this.handlerExceptionResolvers = Arrays.asList(exceptionResolvers);
return this;
}
/**
* Set up view resolution with the given {@link ViewResolver}s.
* If not set, an {@link InternalResourceViewResolver} is used by default.
*/
public StandaloneMockMvcBuilder setViewResolvers(ViewResolver...resolvers) {
this.viewResolvers = Arrays.asList(resolvers);
return this;
}
/**
* Sets up a single {@link ViewResolver} that always returns the provided
* view instance. This is a convenient shortcut if you need to use one
* View instance only -- e.g. rendering generated content (JSON, XML, Atom).
*/
public StandaloneMockMvcBuilder setSingleView(View view) {
this.viewResolvers = Collections.<ViewResolver>singletonList(new StaticViewResolver(view));
return this;
}
/**
* Provide a LocaleResolver instance.
* If not provided, the default one used is {@link AcceptHeaderLocaleResolver}.
*/
public StandaloneMockMvcBuilder setLocaleResolver(LocaleResolver localeResolver) {
this.localeResolver = localeResolver;
return this;
}
/**
* Provide a custom FlashMapManager instance.
* If not provided, {@code SessionFlashMapManager} is used by default.
*/
public StandaloneMockMvcBuilder setFlashMapManager(FlashMapManager flashMapManager) {
this.flashMapManager = flashMapManager;
return this;
}
/**
* Whether to use suffix pattern match (".*") when matching patterns to
* requests. If enabled a method mapped to "/users" also matches to "/users.*".
* <p>The default value is {@code true}.
*/
public StandaloneMockMvcBuilder setUseSuffixPatternMatch(boolean useSuffixPatternMatch) {
this.useSuffixPatternMatch = useSuffixPatternMatch;
return this;
}
/**
* Whether to match to URLs irrespective of the presence of a trailing slash.
* If enabled a method mapped to "/users" also matches to "/users/".
* <p>The default value is {@code true}.
*/
public StandaloneMockMvcBuilder setUseTrailingSlashPatternMatch(boolean useTrailingSlashPatternMatch) {
this.useTrailingSlashPatternMatch = useTrailingSlashPatternMatch;
return this;
}
/**
* Set if ";" (semicolon) content should be stripped from the request URI. The value,
* if provided, is in turn set on
* {@link AbstractHandlerMapping#setRemoveSemicolonContent(boolean)}.
*/
public StandaloneMockMvcBuilder setRemoveSemicolonContent(boolean removeSemicolonContent) {
this.removeSemicolonContent = removeSemicolonContent;
return this;
}
/**
* In a standalone setup there is no support for placeholder values embedded in
* request mappings. This method allows manually provided placeholder values so they
* can be resolved. Alternatively consider creating a test that initializes a
* {@link WebApplicationContext}.
*/
public StandaloneMockMvcBuilder addPlaceHolderValue(String name, String value) {
this.placeHolderValues.put(name, value);
return this;
}
@Override
protected void initWebAppContext(WebApplicationContext cxt) {
StubWebApplicationContext mockCxt = (StubWebApplicationContext) cxt;
registerMvcSingletons(mockCxt);
cxt.getServletContext().setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, mockCxt);
}
private void registerMvcSingletons(StubWebApplicationContext cxt) {
StandaloneConfiguration config = new StandaloneConfiguration();
StaticRequestMappingHandlerMapping hm = config.getHandlerMapping();
hm.setServletContext(cxt.getServletContext());
hm.setApplicationContext(cxt);
hm.registerHandlers(controllers);
cxt.addBean("requestMappingHandlerMapping", hm);
RequestMappingHandlerAdapter handlerAdapter = config.requestMappingHandlerAdapter();
handlerAdapter.setServletContext(cxt.getServletContext());
handlerAdapter.setApplicationContext(cxt);
handlerAdapter.afterPropertiesSet();
cxt.addBean("requestMappingHandlerAdapter", handlerAdapter);
cxt.addBean("handlerExceptionResolver", config.handlerExceptionResolver());
cxt.addBeans(initViewResolvers(cxt));
cxt.addBean(DispatcherServlet.LOCALE_RESOLVER_BEAN_NAME, this.localeResolver);
cxt.addBean(DispatcherServlet.THEME_RESOLVER_BEAN_NAME, new FixedThemeResolver());
cxt.addBean(DispatcherServlet.REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME, new DefaultRequestToViewNameTranslator());
this.flashMapManager = new SessionFlashMapManager();
cxt.addBean(DispatcherServlet.FLASH_MAP_MANAGER_BEAN_NAME, this.flashMapManager);
}
private List<ViewResolver> initViewResolvers(WebApplicationContext wac) {
this.viewResolvers = (this.viewResolvers == null) ?
Arrays.<ViewResolver>asList(new InternalResourceViewResolver()) : this.viewResolvers;
for (Object viewResolver : this.viewResolvers) {
if (viewResolver instanceof WebApplicationObjectSupport) {
((WebApplicationObjectSupport) viewResolver).setApplicationContext(wac);
}
}
return this.viewResolvers;
}
/** Using the MVC Java configuration as the starting point for the "standalone" setup */
private class StandaloneConfiguration extends WebMvcConfigurationSupport {
public StaticRequestMappingHandlerMapping getHandlerMapping() {
StaticRequestMappingHandlerMapping handlerMapping = new StaticRequestMappingHandlerMapping();
handlerMapping.setEmbeddedValueResolver(new StaticStringValueResolver(placeHolderValues));
handlerMapping.setUseSuffixPatternMatch(useSuffixPatternMatch);
handlerMapping.setUseTrailingSlashMatch(useTrailingSlashPatternMatch);
handlerMapping.setOrder(0);
handlerMapping.setInterceptors(getInterceptors());
if (removeSemicolonContent != null) {
handlerMapping.setRemoveSemicolonContent(removeSemicolonContent);
}
return handlerMapping;
}
@Override
protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.addAll(messageConverters);
}
@Override
protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.addAll(customArgumentResolvers);
}
@Override
protected void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
returnValueHandlers.addAll(customReturnValueHandlers);
}
@Override
protected void addInterceptors(InterceptorRegistry registry) {
for (MappedInterceptor interceptor : mappedInterceptors) {
InterceptorRegistration registration = registry.addInterceptor(interceptor.getInterceptor());
if (interceptor.getPathPatterns() != null) {
registration.addPathPatterns(interceptor.getPathPatterns());
}
}
}
@Override
public ContentNegotiationManager mvcContentNegotiationManager() {
return (contentNegotiationManager != null) ? contentNegotiationManager : super.mvcContentNegotiationManager();
}
@Override
public FormattingConversionService mvcConversionService() {
return (conversionService != null) ? conversionService : super.mvcConversionService();
}
@Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
if (asyncRequestTimeout != null) {
configurer.setDefaultTimeout(asyncRequestTimeout);
}
}
@Override
public Validator mvcValidator() {
Validator mvcValidator = (validator != null) ? validator : super.mvcValidator();
if (mvcValidator instanceof InitializingBean) {
try {
((InitializingBean) mvcValidator).afterPropertiesSet();
}
catch (Exception e) {
throw new BeanInitializationException("Failed to initialize Validator", e);
}
}
return mvcValidator;
}
@Override
protected void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
exceptionResolvers.addAll(StandaloneMockMvcBuilder.this.handlerExceptionResolvers);
}
}
/** A {@code RequestMappingHandlerMapping} that allows registration of controllers */
private static class StaticRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
public void registerHandlers(Object...handlers) {
for (Object handler : handlers) {
super.detectHandlerMethods(handler);
}
}
}
/** A static resolver placeholder for values embedded in request mappings */
private static class StaticStringValueResolver implements StringValueResolver {
private final PropertyPlaceholderHelper helper;
private final PlaceholderResolver resolver;
public StaticStringValueResolver(final Map<String, String> values) {
this.helper = new PropertyPlaceholderHelper("${", "}", ":", false);
this.resolver = new PlaceholderResolver() {
@Override
public String resolvePlaceholder(String placeholderName) {
return values.get(placeholderName);
}
};
}
@Override
public String resolveStringValue(String strVal) throws BeansException {
return this.helper.replacePlaceholders(strVal, this.resolver);
}
}
/** A {@link ViewResolver} that always returns same View */
private static class StaticViewResolver implements ViewResolver {
private final View view;
public StaticViewResolver(View view) {
this.view = view;
}
@Override
public View resolveViewName(String viewName, Locale locale) throws Exception {
return this.view;
}
}
}

View File

@@ -0,0 +1,393 @@
/*
* Copyright 2002-2013 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.servlet.setup;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.servlet.ServletContext;
import org.springframework.beans.BeansException;
import org.springframework.beans.TypeConverter;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.support.StaticListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceResolvable;
import org.springframework.context.NoSuchMessageException;
import org.springframework.context.support.DelegatingMessageSource;
import org.springframework.core.env.Environment;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.util.ObjectUtils;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.ServletContextResourcePatternResolver;
/**
* A mock WebApplicationContext that accepts registrations of object instances.
*
* <p>As registered object instances are instantiated and initialized
* externally, there is no wiring, bean initialization, lifecycle events, as
* well as no pre-processing and post-processing hooks typically associated with
* beans managed by an {@link ApplicationContext}. Just a simple lookup into a
* {@link StaticListableBeanFactory}.
*
* @author Rossen Stoyanchev
* @since 3.2
*/
class StubWebApplicationContext implements WebApplicationContext {
private final ServletContext servletContext;
private final StubBeanFactory beanFactory = new StubBeanFactory();
private final String id = ObjectUtils.identityToString(this);
private final String displayName = ObjectUtils.identityToString(this);
private final long startupDate = System.currentTimeMillis();
private final Environment environment = new StandardEnvironment();
private final MessageSource messageSource = new DelegatingMessageSource();
private final ResourcePatternResolver resourcePatternResolver;
/**
* Class constructor.
*/
public StubWebApplicationContext(ServletContext servletContext) {
this.servletContext = servletContext;
this.resourcePatternResolver = new ServletContextResourcePatternResolver(servletContext);
}
/**
* Returns an instance that can initialize {@link ApplicationContextAware} beans.
*/
@Override
public AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException {
return this.beanFactory;
}
@Override
public ServletContext getServletContext() {
return this.servletContext;
}
//---------------------------------------------------------------------
// Implementation of ApplicationContext interface
//---------------------------------------------------------------------
@Override
public String getId() {
return this.id;
}
@Override
public String getApplicationName() {
return "";
}
@Override
public String getDisplayName() {
return this.displayName;
}
@Override
public long getStartupDate() {
return this.startupDate;
}
@Override
public ApplicationContext getParent() {
return null;
}
@Override
public Environment getEnvironment() {
return this.environment ;
}
public void addBean(String name, Object bean) {
this.beanFactory.addBean(name, bean);
}
public void addBeans(List<?> beans) {
for (Object bean : beans) {
String name = bean.getClass().getName() + "#" + ObjectUtils.getIdentityHexString(bean);
this.beanFactory.addBean(name, bean);
}
}
//---------------------------------------------------------------------
// Implementation of BeanFactory interface
//---------------------------------------------------------------------
@Override
public Object getBean(String name) throws BeansException {
return this.beanFactory.getBean(name);
}
@Override
public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
return this.beanFactory.getBean(name, requiredType);
}
@Override
public <T> T getBean(Class<T> requiredType) throws BeansException {
return this.beanFactory.getBean(requiredType);
}
@Override
public Object getBean(String name, Object... args) throws BeansException {
return this.beanFactory.getBean(name, args);
}
@Override
public boolean containsBean(String name) {
return this.beanFactory.containsBean(name);
}
@Override
public boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
return this.beanFactory.isSingleton(name);
}
@Override
public boolean isPrototype(String name) throws NoSuchBeanDefinitionException {
return this.beanFactory.isPrototype(name);
}
@Override
public boolean isTypeMatch(String name, Class<?> targetType) throws NoSuchBeanDefinitionException {
return this.beanFactory.isTypeMatch(name, targetType);
}
@Override
public Class<?> getType(String name) throws NoSuchBeanDefinitionException {
return this.beanFactory.getType(name);
}
@Override
public String[] getAliases(String name) {
return this.beanFactory.getAliases(name);
}
//---------------------------------------------------------------------
// Implementation of ListableBeanFactory interface
//---------------------------------------------------------------------
@Override
public boolean containsBeanDefinition(String beanName) {
return this.beanFactory.containsBeanDefinition(beanName);
}
@Override
public int getBeanDefinitionCount() {
return this.beanFactory.getBeanDefinitionCount();
}
@Override
public String[] getBeanDefinitionNames() {
return this.beanFactory.getBeanDefinitionNames();
}
@Override
public String[] getBeanNamesForType(Class<?> type) {
return this.beanFactory.getBeanNamesForType(type);
}
@Override
public String[] getBeanNamesForType(Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) {
return this.beanFactory.getBeanNamesForType(type, includeNonSingletons, allowEagerInit);
}
@Override
public <T> Map<String, T> getBeansOfType(Class<T> type) throws BeansException {
return this.beanFactory.getBeansOfType(type);
}
@Override
public <T> Map<String, T> getBeansOfType(Class<T> type, boolean includeNonSingletons, boolean allowEagerInit)
throws BeansException {
return this.beanFactory.getBeansOfType(type, includeNonSingletons, allowEagerInit);
}
@Override
public Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType)
throws BeansException {
return this.beanFactory.getBeansWithAnnotation(annotationType);
}
@Override
public <A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType) {
return this.beanFactory.findAnnotationOnBean(beanName, annotationType);
}
//---------------------------------------------------------------------
// Implementation of HierarchicalBeanFactory interface
//---------------------------------------------------------------------
@Override
public BeanFactory getParentBeanFactory() {
return null;
}
@Override
public boolean containsLocalBean(String name) {
return this.beanFactory.containsBean(name);
}
//---------------------------------------------------------------------
// Implementation of MessageSource interface
//---------------------------------------------------------------------
@Override
public String getMessage(String code, Object args[], String defaultMessage, Locale locale) {
return this.messageSource.getMessage(code, args, defaultMessage, locale);
}
@Override
public String getMessage(String code, Object args[], Locale locale) throws NoSuchMessageException {
return this.messageSource.getMessage(code, args, locale);
}
@Override
public String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException {
return this.messageSource.getMessage(resolvable, locale);
}
//---------------------------------------------------------------------
// Implementation of ResourceLoader interface
//---------------------------------------------------------------------
@Override
public ClassLoader getClassLoader() {
return null;
}
@Override
public Resource getResource(String location) {
return this.resourcePatternResolver.getResource(location);
}
//---------------------------------------------------------------------
// Other
//---------------------------------------------------------------------
@Override
public void publishEvent(ApplicationEvent event) {
}
@Override
public Resource[] getResources(String locationPattern) throws IOException {
return this.resourcePatternResolver.getResources(locationPattern);
}
/**
* An extension of StaticListableBeanFactory that implements
* AutowireCapableBeanFactory in order to allow bean initialization of
* {@link ApplicationContextAware} singletons.
*/
private class StubBeanFactory extends StaticListableBeanFactory implements AutowireCapableBeanFactory {
@Override
public Object initializeBean(Object existingBean, String beanName) throws BeansException {
if (existingBean instanceof ApplicationContextAware) {
((ApplicationContextAware) existingBean).setApplicationContext(StubWebApplicationContext.this);
}
return existingBean;
}
@Override
public <T> T createBean(Class<T> beanClass) {
throw new UnsupportedOperationException();
}
@Override
@SuppressWarnings("rawtypes")
public Object createBean(Class beanClass, int autowireMode, boolean dependencyCheck) {
throw new UnsupportedOperationException();
}
@Override
@SuppressWarnings("rawtypes")
public Object autowire(Class beanClass, int autowireMode, boolean dependencyCheck) {
throw new UnsupportedOperationException();
}
@Override
public void autowireBean(Object existingBean) throws BeansException {
throw new UnsupportedOperationException();
}
@Override
public void autowireBeanProperties(Object existingBean, int autowireMode, boolean dependencyCheck) {
throw new UnsupportedOperationException();
}
@Override
public Object configureBean(Object existingBean, String beanName) {
throw new UnsupportedOperationException();
}
@Override
public Object resolveDependency(DependencyDescriptor descriptor, String beanName) {
throw new UnsupportedOperationException();
}
@Override
public Object resolveDependency(DependencyDescriptor descriptor, String beanName,
Set<String> autowiredBeanNames, TypeConverter typeConverter) {
throw new UnsupportedOperationException();
}
@Override
public void applyBeanPropertyValues(Object existingBean, String beanName) throws BeansException {
throw new UnsupportedOperationException();
}
@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) {
throw new UnsupportedOperationException();
}
@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) {
throw new UnsupportedOperationException();
}
@Override
public void destroyBean(Object existingBean) {
throw new UnsupportedOperationException();
}
}
}

View File

@@ -0,0 +1,22 @@
/*
* Copyright 2002-2012 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.
*/
/**
* Contains built-in {@code MockMvcBuilder} implementations.
* Use {@link org.springframework.test.web.servlet.setup.MockMvcBuilders}
* to access to instances of those implementations.
*/
package org.springframework.test.web.servlet.setup;