MockClientHttpRequest and
+ * MockClientHttpResponse.
+ */
+package org.springframework.mock.http.client;
+
diff --git a/src/main/java/org/springframework/mock/http/package-info.java b/src/main/java/org/springframework/mock/http/package-info.java
new file mode 100644
index 0000000..6f0868a
--- /dev/null
+++ b/src/main/java/org/springframework/mock/http/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2011-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.
+ */
+
+/**
+ * Mock implementations of client/server-side HTTP abstractions.
+ * This package contains MockHttpInputMessage and
+ * MockHttpOutputMessage.
+ */
+package org.springframework.mock.http;
+
diff --git a/src/main/java/org/springframework/test/web/AssertionErrors.java b/src/main/java/org/springframework/test/web/AssertionErrors.java
index 2f4d0fd..cc10978 100644
--- a/src/main/java/org/springframework/test/web/AssertionErrors.java
+++ b/src/main/java/org/springframework/test/web/AssertionErrors.java
@@ -15,11 +15,9 @@
*/
package org.springframework.test.web;
-import java.util.Map;
-
/**
* JUnit independent assertion class.
- *
+ *
* @author Lukas Krecan
* @author Arjen Poutsma
*/
@@ -39,7 +37,7 @@ public static void fail(String message) {
/**
* Fails a test with the given message passing along expected and actual values to be added to the message.
- *
+ *
* @param message the message
* @param expected the expected value
* @param actual the actual value
@@ -76,33 +74,5 @@ public static void assertEquals(String message, Object expected, Object actual)
}
fail(message, expected, actual);
}
-
- /**
- * Assert the given names are present in the provided map of named values.
- * @param label a label describing the named value - e.g. "Request attribute", "Response header", etc.
- * @param namedValues a map containing all name-value pairs
- * @param names the names to check
- */
- public static void assertNameValuesPresent(String label, MapBelow is an example: + *
+ * 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);
+ * // Use the hotel instance...
+ *
+ * mockServer.verify();
+ *
+ * To create an instance of this class, use {@link #createServer(RestTemplate)}
+ * and provide the {@code RestTemplate} to set up for the mock testing.
+ *
+ *
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 RequestMatchers}
+ * and the {@code ResponseCreator} implementations provided in
+ * {@link ResponseCreators} both of which can be statically imported.
+ *
+ *
At the end of the test use {@link #verify()} to ensure all expected
+ * requests were actually performed.
+ *
+ *
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.
+ *
+ *
Credits: 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
+ */
+public class MockRestServiceServer {
+
+ private final List expectedRequests =
+ new LinkedList();
+
+ private final List actualRequests =
+ new LinkedList();
+
+
+ /**
+ * 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.
+ *
+ * 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 RequestMatchers}
+ * @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 requestIterator;
+
+ 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;
+ }
+ }
+
+}
diff --git a/src/main/java/org/springframework/test/web/client/RequestMatcher.java b/src/main/java/org/springframework/test/web/client/RequestMatcher.java
new file mode 100644
index 0000000..085ff06
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/client/RequestMatcher.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2011 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
+ */
+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;
+
+}
diff --git a/src/main/java/org/springframework/test/web/client/RequestMatcherClientHttpRequest.java b/src/main/java/org/springframework/test/web/client/RequestMatcherClientHttpRequest.java
new file mode 100644
index 0000000..a5a2236
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/client/RequestMatcherClientHttpRequest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2011-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
+ */
+class RequestMatcherClientHttpRequest extends MockClientHttpRequest implements ResponseActions {
+
+ private final List requestMatchers = new LinkedList();
+
+ private ResponseCreator responseCreator;
+
+
+ public RequestMatcherClientHttpRequest(RequestMatcher requestMatcher) {
+ Assert.notNull(requestMatcher, "RequestMatcher is required");
+ this.requestMatchers.add(requestMatcher);
+ }
+
+ public ResponseActions andExpect(RequestMatcher requestMatcher) {
+ Assert.notNull(requestMatcher, "RequestMatcher is required");
+ this.requestMatchers.add(requestMatcher);
+ return this;
+ }
+
+ public void andRespond(ResponseCreator responseCreator) {
+ Assert.notNull(responseCreator, "ResponseCreator is required");
+ this.responseCreator = responseCreator;
+ }
+
+ public ClientHttpResponse execute() 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.execute();
+ }
+
+}
diff --git a/src/main/java/org/springframework/test/web/client/ResponseActions.java b/src/main/java/org/springframework/test/web/client/ResponseActions.java
new file mode 100644
index 0000000..ba80a77
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/client/ResponseActions.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2011 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
+ */
+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);
+
+}
diff --git a/src/main/java/org/springframework/test/web/client/ResponseCreator.java b/src/main/java/org/springframework/test/web/client/ResponseCreator.java
new file mode 100644
index 0000000..d3c3f57
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/client/ResponseCreator.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2011 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.ResponseCreators;
+
+/**
+ * A contract for creating a {@link ClientHttpResponse}.
+ * Implementations can be obtained via {@link ResponseCreators}.
+ *
+ * @author Craig Walls
+ */
+public interface ResponseCreator {
+
+ /**
+ * Create a response for the given request.
+ * @param request the request
+ */
+ ClientHttpResponse createResponse(ClientHttpRequest request) throws IOException;
+
+}
diff --git a/src/main/java/org/springframework/test/web/client/match/ContentRequestMatchers.java b/src/main/java/org/springframework/test/web/client/match/ContentRequestMatchers.java
new file mode 100644
index 0000000..a78a53c
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/client/match/ContentRequestMatchers.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright 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.web.AssertionErrors.assertEquals;
+import static org.springframework.test.web.AssertionErrors.assertTrue;
+
+import java.io.IOException;
+
+import javax.xml.transform.Source;
+import javax.xml.transform.dom.DOMSource;
+
+import org.hamcrest.Matcher;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.springframework.http.MediaType;
+import org.springframework.http.client.ClientHttpRequest;
+import org.springframework.mock.http.client.MockClientHttpRequest;
+import org.springframework.test.web.client.RequestMatcher;
+import org.springframework.test.web.support.XmlExpectationsHelper;
+import org.w3c.dom.Node;
+
+/**
+ * Factory for request content {@code RequestMatcher}'s. An instance of this
+ * class is typically accessed via {@link RequestMatchers#content()}.
+ *
+ * @author Rossen Stoyanchev
+ */
+public class ContentRequestMatchers {
+
+ private final XmlExpectationsHelper xmlHelper;
+
+
+ /**
+ * Class constructor, not for direct instantiation.
+ * Use {@link RequestMatchers#content()}.
+ */
+ protected ContentRequestMatchers() {
+ this.xmlHelper = new XmlExpectationsHelper();
+ }
+
+ /**
+ * Assert the request content type as a String.
+ */
+ public RequestMatcher mimeType(String expectedContentType) {
+ return mimeType(MediaType.parseMediaType(expectedContentType));
+ }
+
+ /**
+ * Assert the request content type as a {@link MediaType}.
+ */
+ public RequestMatcher mimeType(final MediaType expectedContentType) {
+ return new RequestMatcher() {
+ 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);
+ }
+ };
+ }
+
+ /**
+ * 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() {
+ public void match(ClientHttpRequest request) throws IOException, AssertionError {
+ MockClientHttpRequest mockRequest = (MockClientHttpRequest) request;
+ MatcherAssert.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(String expectedContent) {
+ return string(Matchers.equalTo(expectedContent));
+ }
+
+ /**
+ * Compare the body of the request to the given byte array.
+ */
+ public RequestMatcher bytes(final byte[] expectedContent) {
+ return new RequestMatcher() {
+ public void match(ClientHttpRequest request) throws IOException, AssertionError {
+ MockClientHttpRequest mockRequest = (MockClientHttpRequest) request;
+ byte[] content = mockRequest.getBodyAsBytes();
+ MatcherAssert.assertThat("Request content", content, Matchers.equalTo(expectedContent));
+ }
+ };
+ }
+
+ /**
+ * 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.
+ *
+ * Use of this matcher assumes the
+ * XMLUnit 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 http://code.google.com/p/xml-matchers/
+ */
+ 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 {
+
+ 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;
+
+ }
+}
diff --git a/src/main/java/org/springframework/test/web/client/match/JsonPathRequestMatchers.java b/src/main/java/org/springframework/test/web/client/match/JsonPathRequestMatchers.java
new file mode 100644
index 0000000..b7ad09f
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/client/match/JsonPathRequestMatchers.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2011-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.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.instanceOf;
+
+import java.io.IOException;
+import java.text.ParseException;
+import java.util.List;
+
+import org.hamcrest.Matcher;
+import org.springframework.http.client.ClientHttpRequest;
+import org.springframework.mock.http.client.MockClientHttpRequest;
+import org.springframework.test.web.client.RequestMatcher;
+import org.springframework.test.web.support.JsonPathExpectationsHelper;
+
+/**
+ * Factory methods for request content {@code RequestMatcher}'s using a JSONPath expression.
+ * An instance of this class is typically accessed via
+ * {@code RequestMatchers.jsonPath(..)}.
+ *
+ * @author Rossen Stoyanchev
+ */
+public class JsonPathRequestMatchers {
+
+ private JsonPathExpectationsHelper jsonPathHelper;
+
+
+ /**
+ * Class constructor, not for direct instantiation. Use
+ * {@link RequestMatchers#jsonPath(String, Matcher)} or
+ * {@link RequestMatchers#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 RequestMatcher value(final Matcher 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(Object expectedValue) {
+ return value(equalTo(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 value(instanceOf(List.class));
+ }
+
+
+ /**
+ * Abstract base class for JSONPath {@link RequestMatcher}'s.
+ */
+ private abstract static class AbstractJsonPathRequestMatcher implements RequestMatcher {
+
+ 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;
+
+ }
+}
diff --git a/src/main/java/org/springframework/test/web/client/match/RequestMatchers.java b/src/main/java/org/springframework/test/web/client/match/RequestMatchers.java
new file mode 100644
index 0000000..156aeaf
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/client/match/RequestMatchers.java
@@ -0,0 +1,262 @@
+/*
+ * Copyright 2011-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.net.URI;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.xpath.XPathExpressionException;
+
+import org.hamcrest.Matcher;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.hamcrest.core.IsEqual;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.client.ClientHttpRequest;
+import org.springframework.mock.http.client.MockClientHttpRequest;
+import org.springframework.test.web.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)}.
+ *
+ * Eclipse users: consider adding this class as a Java editor
+ * favorite. To navigate, open the Preferences and type "favorites".
+ *
+ * @author Craig Walls
+ * @author Rossen Stoyanchev
+ */
+public abstract class RequestMatchers {
+
+
+ /**
+ * Private class constructor.
+ */
+ private RequestMatchers() {
+ }
+
+ /**
+ * Match to any request.
+ */
+ public static RequestMatcher anything() {
+ return new RequestMatcher() {
+ 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 matcher) {
+ Assert.notNull(matcher, "'matcher' must not be null");
+ return new RequestMatcher() {
+ public void match(ClientHttpRequest request) throws IOException, AssertionError {
+ MatcherAssert.assertThat("Request URI", request.getURI().toString(), matcher);
+ }
+ };
+ }
+
+ /**
+ * Assert the request URI string.
+ *
+ * @param uri the expected URI
+ * @return the request matcher
+ */
+ public static RequestMatcher requestTo(String uri) {
+ Assert.notNull(uri, "'uri' must not be null");
+ return requestTo(Matchers.equalTo(uri));
+ }
+
+ /**
+ * 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() {
+ 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() {
+ 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() {
+ public void match(ClientHttpRequest request) {
+ HttpHeaders headers = request.getHeaders();
+ List values = headers.get(name);
+ AssertionErrors.assertTrue("Expected header <" + name + ">", values != null);
+ AssertionErrors.assertTrue("Expected header <" + name + "> to have at least <" + matchers.length
+ + "> values but it has only <" + values.size() + ">", matchers.length <= values.size());
+ for (int i = 0 ; i < matchers.length; i++) {
+ MatcherAssert.assertThat("Request header", headers.get(name).get(i), matchers[i]);
+ }
+ }
+ };
+ }
+
+ /**
+ * Assert request header values.
+ */
+ public static RequestMatcher header(String name, String... values) {
+ @SuppressWarnings("unchecked")
+ Matcher super String>[] matchers = new IsEqual[values.length];
+ for (int i = 0; i < values.length; i++) {
+ matchers[i] = Matchers.equalTo(values[i]);
+ }
+ return header(name, matchers);
+ }
+
+ /**
+ * Access to request body matchers.
+ */
+ public static ContentRequestMatchers content() {
+ return new ContentRequestMatchers();
+ }
+
+ /**
+ * Access to request body matchers using a JSONPath 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 JSONPath 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 RequestMatcher jsonPath(String expression, Matcher 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 namespaces, Object... args)
+ throws XPathExpressionException {
+
+ return new XpathRequestMatchers(expression, namespaces, args);
+ }
+
+
+ // Deprecated methods ..
+
+ /**
+ * Expect that the specified request header contains a subtring
+ *
+ * @deprecated in favor of {@link #header(String, Matcher...)}
+ */
+ public static RequestMatcher headerContains(final String header, final String substring) {
+ Assert.notNull(header, "'header' must not be null");
+ Assert.notNull(substring, "'substring' must not be null");
+ return new RequestMatcher() {
+ public void match(ClientHttpRequest request) throws AssertionError {
+ List actualHeaders = request.getHeaders().get(header);
+ AssertionErrors.assertTrue("Expected header <" + header + "> in request", actualHeaders != null);
+
+ boolean foundMatch = false;
+ for (String headerValue : actualHeaders) {
+ if (headerValue.contains(substring)) {
+ foundMatch = true;
+ break;
+ }
+ }
+
+ AssertionErrors.assertTrue("Expected value containing <" + substring + "> in header <" + header + ">",
+ foundMatch);
+ }
+ };
+ }
+
+ /**
+ * Expect the given request body content.
+ *
+ * @deprecated in favor of {@link #content()} as well as {@code jsonPath(..)},
+ * and {@code xpath(..)} methods in this class
+ */
+ public static RequestMatcher body(final String body) {
+ Assert.notNull(body, "'body' must not be null");
+ return new RequestMatcher() {
+ public void match(ClientHttpRequest request) throws AssertionError, IOException {
+ MockClientHttpRequest mockRequest = (MockClientHttpRequest) request;
+ AssertionErrors.assertEquals("Unexpected body content", body, mockRequest.getBodyAsString());
+ }
+ };
+ }
+
+}
diff --git a/src/main/java/org/springframework/test/web/client/match/XpathRequestMatchers.java b/src/main/java/org/springframework/test/web/client/match/XpathRequestMatchers.java
new file mode 100644
index 0000000..1e65808
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/client/match/XpathRequestMatchers.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2011-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.hamcrest.Matchers;
+import org.springframework.http.client.ClientHttpRequest;
+import org.springframework.mock.http.client.MockClientHttpRequest;
+import org.springframework.test.web.client.RequestMatcher;
+import org.springframework.test.web.support.XpathExpectationsHelper;
+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
+ */
+public class XpathRequestMatchers {
+
+ private final XpathExpectationsHelper xpathHelper;
+
+
+ /**
+ * Class constructor, not for direct instantiation. Use
+ * {@link RequestMatchers#xpath(String, Object...)} or
+ * {@link RequestMatchers#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 namespaces, Object ... args)
+ throws XPathExpressionException {
+
+ this.xpathHelper = new XpathExpectationsHelper(expression, namespaces, args);
+ }
+
+ /**
+ * Apply the XPath and assert it with the given {@code Matcher}.
+ */
+ public 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 RequestMatcher exists() {
+ return node(Matchers.notNullValue());
+ }
+
+ /**
+ * Assert that content does not exist at the given XPath.
+ */
+ public RequestMatcher doesNotExist() {
+ return node(Matchers.nullValue());
+ }
+
+ /**
+ * Apply the XPath and assert the number of nodes found with the given
+ * {@code Matcher}.
+ */
+ public RequestMatcher nodeCount(final Matcher 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 RequestMatcher nodeCount(int expectedCount) {
+ return nodeCount(Matchers.equalTo(expectedCount));
+ }
+
+ /**
+ * Apply the XPath and assert the String content found with the given matcher.
+ */
+ public 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(String value) {
+ return string(Matchers.equalTo(value));
+ }
+
+ /**
+ * Apply the XPath and assert the number found with the given matcher.
+ */
+ public 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(Double value) {
+ return number(Matchers.equalTo(value));
+ }
+
+ /**
+ * Apply the XPath and assert the boolean value found.
+ */
+ public 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 {
+
+ 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;
+
+ }
+
+}
diff --git a/src/main/java/org/springframework/test/web/client/match/package-info.java b/src/main/java/org/springframework/test/web/client/match/package-info.java
new file mode 100644
index 0000000..69b5bb5
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/client/match/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2011-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.RequestMatchers}
+ * to gain access to instances of those implementations.
+ */
+package org.springframework.test.web.client.match;
diff --git a/src/main/java/org/springframework/test/web/client/package-info.java b/src/main/java/org/springframework/test/web/client/package-info.java
new file mode 100644
index 0000000..c331fcc
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/client/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2011-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;
diff --git a/src/main/java/org/springframework/test/web/client/response/DefaultResponseCreator.java b/src/main/java/org/springframework/test/web/client/response/DefaultResponseCreator.java
new file mode 100644
index 0000000..9e0eeee
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/client/response/DefaultResponseCreator.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 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
+ */
+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 ResponseCreators}.
+ */
+ protected DefaultResponseCreator(HttpStatus statusCode) {
+ Assert.notNull(statusCode);
+ this.statusCode = statusCode;
+ }
+
+ 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;
+ }
+
+}
diff --git a/src/main/java/org/springframework/test/web/client/response/ResponseCreators.java b/src/main/java/org/springframework/test/web/client/response/ResponseCreators.java
new file mode 100644
index 0000000..4edac3d
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/client/response/ResponseCreators.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2011-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.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.mock.http.client.MockClientHttpResponse;
+import org.springframework.test.web.client.ResponseCreator;
+
+/**
+ * Static factory methods for obtaining a {@link ResponseCreator} instance.
+ *
+ * Eclipse users: consider adding this class as a Java editor
+ * favorite. To navigate, open the Preferences and type "favorites".
+ *
+ * @author Rossen Stoyanchev
+ */
+public abstract class ResponseCreators {
+
+ /**
+ * Private class constructor.
+ */
+ private ResponseCreators() {
+ }
+
+ /**
+ * {@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 mediaType 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 mediaType 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);
+ }
+
+ /**
+ * Respond with a given body, headers, status code, and status text.
+ *
+ * @param body the body of the response "UTF-8" encoded
+ * @param headers the response headers
+ * @param statusCode the response status code
+ * @param statusText the response status text
+ *
+ * @deprecated in favor of methods returning DefaultResponseCreator
+ */
+ public static ResponseCreator withResponse(final String body, final HttpHeaders headers,
+ final HttpStatus statusCode, final String statusText) {
+
+ return new ResponseCreator() {
+ public MockClientHttpResponse createResponse(ClientHttpRequest request) throws IOException {
+ MockClientHttpResponse response = new MockClientHttpResponse(body.getBytes("UTF-8"), statusCode);
+ response.getHeaders().putAll(headers);
+ return response;
+ }
+ };
+ }
+
+ /**
+ * Respond with the given body, headers, and a status code of 200 (OK).
+ *
+ * @param body the body of the response "UTF-8" encoded
+ * @param headers the response headers
+ *
+ * @deprecated in favor of methods 'withXyz' in this class returning DefaultResponseCreator
+ */
+ public static ResponseCreator withResponse(String body, HttpHeaders headers) {
+ return withResponse(body, headers, HttpStatus.OK, "");
+ }
+
+ /**
+ * Respond with a given body, headers, status code, and text.
+ *
+ * @param body a {@link Resource} containing the body of the response
+ * @param headers the response headers
+ * @param statusCode the response status code
+ * @param statusText the response status text
+ *
+ * @deprecated in favor of methods 'withXyz' in this class returning DefaultResponseCreator
+ */
+ public static ResponseCreator withResponse(final Resource body, final HttpHeaders headers,
+ final HttpStatus statusCode, String statusText) {
+
+ return new ResponseCreator() {
+ public MockClientHttpResponse createResponse(ClientHttpRequest request) throws IOException {
+ MockClientHttpResponse response = new MockClientHttpResponse(body.getInputStream(), statusCode);
+ response.getHeaders().putAll(headers);
+ return response;
+ }
+ };
+ }
+
+ /**
+ * Respond with the given body, headers, and a status code of 200 (OK).
+ * @param body the body of the response
+ * @param headers the response headers
+ *
+ * @deprecated in favor of methods 'withXyz' in this class returning DefaultResponseCreator
+ */
+ public static ResponseCreator withResponse(final Resource body, final HttpHeaders headers) {
+ return withResponse(body, headers, HttpStatus.OK, "");
+ }
+
+}
diff --git a/src/main/java/org/springframework/test/web/client/response/package-info.java b/src/main/java/org/springframework/test/web/client/response/package-info.java
new file mode 100644
index 0000000..e09bd76
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/client/response/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2011-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.ResponseCreators}
+ * to gain access to instances of those implementations.
+ */
+package org.springframework.test.web.client.response;
diff --git a/src/main/java/org/springframework/test/web/server/AbstractMockMvcBuilder.java b/src/main/java/org/springframework/test/web/server/AbstractMockMvcBuilder.java
deleted file mode 100644
index e417c22..0000000
--- a/src/main/java/org/springframework/test/web/server/AbstractMockMvcBuilder.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright 2002-2011 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.server;
-
-import java.util.Collections;
-import java.util.List;
-
-import javax.servlet.ServletContext;
-
-import org.springframework.web.context.WebApplicationContext;
-import org.springframework.web.servlet.HandlerAdapter;
-import org.springframework.web.servlet.HandlerExceptionResolver;
-import org.springframework.web.servlet.HandlerMapping;
-import org.springframework.web.servlet.LocaleResolver;
-import org.springframework.web.servlet.RequestToViewNameTranslator;
-import org.springframework.web.servlet.ViewResolver;
-
-/**
- * A base class that supports assembling an {@link MvcSetup} to build a {@link MockMvc} instance.
- *
- */
-public abstract class AbstractMockMvcBuilder {
-
- private WebApplicationContext applicationContext;
-
- private List handlerMappings;
-
- private List handlerAdapters;
-
- private List exceptionResolvers;
-
- private List viewResolvers;
-
- private RequestToViewNameTranslator viewNameTranslator;
-
- private LocaleResolver localeResolver;
-
- public final MockMvc build() {
-
- applicationContext = initApplicationContext();
- ServletContext servletContext = applicationContext.getServletContext();
-
- String name = WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE;
- servletContext.setAttribute(name, applicationContext);
-
- handlerMappings = Collections.unmodifiableList(initHandlerMappings());
- handlerAdapters = Collections.unmodifiableList(initHandlerAdapters());
- exceptionResolvers = Collections.unmodifiableList(initHandlerExceptionResolvers());
- viewResolvers = Collections.unmodifiableList(initViewResolvers());
- viewNameTranslator = initViewNameTranslator();
- localeResolver = initLocaleResolver();
-
- MvcSetup mvcSetup = createMvcSetup();
- MockDispatcher mockDispatcher = new MockDispatcher(mvcSetup);
-
- return new MockMvc(servletContext, mockDispatcher);
- }
-
- protected abstract WebApplicationContext initApplicationContext();
-
- protected abstract List extends HandlerMapping> initHandlerMappings();
-
- protected abstract List extends HandlerAdapter> initHandlerAdapters();
-
- protected abstract List extends HandlerExceptionResolver> initHandlerExceptionResolvers();
-
- protected abstract List extends ViewResolver> initViewResolvers();
-
- protected abstract RequestToViewNameTranslator initViewNameTranslator();
-
- protected abstract LocaleResolver initLocaleResolver();
-
- private MvcSetup createMvcSetup() {
-
- return new MvcSetup() {
-
- public List getHandlerMappings() {
- return handlerMappings;
- }
-
- public List getHandlerAdapters() {
- return handlerAdapters;
- }
-
- public List getViewResolvers() {
- return viewResolvers;
- }
-
- public List getExceptionResolvers() {
- return exceptionResolvers;
- }
-
- public RequestToViewNameTranslator getViewNameTranslator() {
- return viewNameTranslator;
- }
-
- public LocaleResolver getLocaleResolver() {
- return localeResolver;
- }
- };
- }
-}
diff --git a/src/main/java/org/springframework/test/web/server/DefaultMockHttpServletRequestBuilder.java b/src/main/java/org/springframework/test/web/server/DefaultMockHttpServletRequestBuilder.java
deleted file mode 100644
index 0cfdb22..0000000
--- a/src/main/java/org/springframework/test/web/server/DefaultMockHttpServletRequestBuilder.java
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright 2002-2011 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.server;
-
-import java.net.URI;
-import java.security.Principal;
-import java.util.Arrays;
-import java.util.LinkedHashMap;
-import java.util.Locale;
-import java.util.Map;
-import javax.servlet.ServletContext;
-import javax.servlet.http.Cookie;
-
-import org.springframework.http.HttpMethod;
-import org.springframework.http.MediaType;
-import org.springframework.mock.web.MockHttpServletRequest;
-import org.springframework.util.Assert;
-import org.springframework.util.LinkedMultiValueMap;
-import org.springframework.util.MultiValueMap;
-
-/**
- * A command class to build and execute a request. Use methods on {@link MockMvc} to obtain a new {@link
- * DefaultMockHttpServletRequestBuilder} instance.
- */
-public class DefaultMockHttpServletRequestBuilder implements MockHttpServletRequestBuilder {
-
- private final URI uri;
-
- private final HttpMethod method;
-
- private final MultiValueMap parameters = new LinkedMultiValueMap();
-
- private final MultiValueMap headers = new LinkedMultiValueMap();
-
- private String contentType;
-
- private byte[] requestBody;
-
- private Cookie[] cookies;
-
- private Locale locale;
-
- private String characterEncoding;
-
- private final Map attributes = new LinkedHashMap();
-
- private final Map sessionAttributes = new LinkedHashMap();
-
- private Principal principal;
-
- /** Use methods on {@link MockMvc} to obtain a new instance. */
- DefaultMockHttpServletRequestBuilder(URI uri, HttpMethod method) {
- this.uri = uri;
- this.method = method;
- }
-
- public DefaultMockHttpServletRequestBuilder param(String name, String value, String... values) {
- addToMultiValueMap(parameters, name, value, values);
- return this;
- }
-
- public DefaultMockHttpServletRequestBuilder accept(MediaType mediaType, MediaType... mediaTypes) {
- addToMultiValueMap(headers, "Accept", mediaType, mediaTypes);
- return this;
- }
-
- public DefaultMockHttpServletRequestBuilder contentType(MediaType mediaType) {
- Assert.notNull(mediaType, "'mediaType' must not be null");
- this.contentType = mediaType.toString();
- headers.set("Content-Type", mediaType);
- return this;
- }
-
- public DefaultMockHttpServletRequestBuilder body(byte[] requestBody) {
- this.requestBody = requestBody;
- return this;
- }
-
- public DefaultMockHttpServletRequestBuilder header(String name, Object value, Object... values) {
- addToMultiValueMap(headers, name, value, values);
- return this;
- }
-
- public DefaultMockHttpServletRequestBuilder cookie(Cookie cookie, Cookie... cookies) {
- Assert.notNull(cookie, "'cookie' must not be null");
- if (cookies == null) {
- this.cookies = new Cookie[]{cookie};
- }
- else {
- this.cookies = new Cookie[1 + cookies.length];
- this.cookies[0] = cookie;
- System.arraycopy(cookies, 0, this.cookies, 1, cookies.length);
- }
- return this;
- }
-
- public DefaultMockHttpServletRequestBuilder locale(Locale locale) {
- this.locale = locale;
- return this;
- }
-
- public DefaultMockHttpServletRequestBuilder characterEncoding(String characterEncoding) {
- this.characterEncoding = characterEncoding;
- return this;
- }
-
- public DefaultMockHttpServletRequestBuilder requestAttr(String name, Object value) {
- Assert.hasLength(name, "'name' must not be empty");
- Assert.notNull(value, "'value' must not be null");
- attributes.put(name, value);
- return this;
- }
-
- public DefaultMockHttpServletRequestBuilder sessionAttr(String name, Object value) {
- Assert.hasLength(name, "'name' must not be empty");
- Assert.notNull(value, "'value' must not be null");
- sessionAttributes.put(name, value);
- return this;
- }
-
- public DefaultMockHttpServletRequestBuilder principal(Principal principal) {
- Assert.notNull(principal, "'principal' must not be null");
- this.principal = principal;
- return this;
- }
-
- public MockHttpServletRequest buildRequest(ServletContext servletContext) {
-
- MockHttpServletRequest request = createServletRequest(servletContext);
-
- request.setMethod(method.name());
- request.setRequestURI(uri.toString());
-
- for (String name : parameters.keySet()) {
- for (String value : parameters.get(name)) {
- request.addParameter(name, value);
- }
- }
- for (String name : headers.keySet()) {
- for (Object value : headers.get(name)) {
- request.addHeader(name, value);
- }
- }
- for (String name : attributes.keySet()) {
- request.setAttribute(name, attributes.get(name));
- }
- for (String name : sessionAttributes.keySet()) {
- request.getSession().setAttribute(name, sessionAttributes.get(name));
- }
-
- request.setContentType(contentType);
- request.setContent(requestBody);
- request.setCookies(cookies);
- request.setCharacterEncoding(characterEncoding);
- request.setUserPrincipal(principal);
-
- if (locale != null) {
- request.addPreferredLocale(locale);
- }
-
- return request;
- }
-
- /**
- * Creates a new {@link MockHttpServletRequest} based on the given {@link ServletContext}. Can be overridden in
- * subclasses.
- *
- * @param servletContext the servlet context to use
- * @return the created mock request
- */
- protected MockHttpServletRequest createServletRequest(ServletContext servletContext) {
- return new MockHttpServletRequest(servletContext);
- }
-
- private static void addToMultiValueMap(MultiValueMap map, String name, T value, T[] values) {
- Assert.hasLength(name, "'name' must not be empty");
- Assert.notNull(value, "'value' must not be null");
- map.add(name, value);
- if (values != null) {
- map.get(name).addAll(Arrays.asList(values));
- }
- }
-
-}
diff --git a/src/main/java/org/springframework/test/web/server/DefaultMvcResult.java b/src/main/java/org/springframework/test/web/server/DefaultMvcResult.java
new file mode 100644
index 0000000..431377b
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/server/DefaultMvcResult.java
@@ -0,0 +1,98 @@
+/*
+ * 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.server;
+
+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
+ */
+class DefaultMvcResult implements MvcResult {
+
+ private final MockHttpServletRequest mockRequest;
+
+ private final MockHttpServletResponse mockResponse;
+
+ private Object handler;
+
+ private HandlerInterceptor[] interceptors;
+
+ private ModelAndView modelAndView;
+
+ private Exception resolvedException;
+
+
+ /**
+ * Create a new instance with the given request and response.
+ */
+ public DefaultMvcResult(MockHttpServletRequest request, MockHttpServletResponse response) {
+ this.mockRequest = request;
+ this.mockResponse = response;
+ }
+
+ public MockHttpServletResponse getResponse() {
+ return mockResponse;
+ }
+
+ public MockHttpServletRequest getRequest() {
+ return mockRequest;
+ }
+
+ public Object getHandler() {
+ return this.handler;
+ }
+
+ public void setHandler(Object handler) {
+ this.handler = handler;
+ }
+
+ public HandlerInterceptor[] getInterceptors() {
+ return this.interceptors;
+ }
+
+ public void setInterceptors(HandlerInterceptor[] interceptors) {
+ this.interceptors = interceptors;
+ }
+
+ public Exception getResolvedException() {
+ return this.resolvedException;
+ }
+
+ public void setResolvedException(Exception resolvedException) {
+ this.resolvedException = resolvedException;
+ }
+
+ public ModelAndView getModelAndView() {
+ return this.modelAndView;
+ }
+
+ public void setModelAndView(ModelAndView mav) {
+ this.modelAndView = mav;
+ }
+
+ public FlashMap getFlashMap() {
+ return RequestContextUtils.getOutputFlashMap(mockRequest);
+ }
+
+}
diff --git a/src/main/java/org/springframework/test/web/server/MockDispatcher.java b/src/main/java/org/springframework/test/web/server/MockDispatcher.java
deleted file mode 100644
index 23b83ba..0000000
--- a/src/main/java/org/springframework/test/web/server/MockDispatcher.java
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Copyright 2002-2011 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.server;
-
-import static org.springframework.test.web.AssertionErrors.fail;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Locale;
-
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.springframework.mock.web.MockHttpServletRequest;
-import org.springframework.mock.web.MockHttpServletResponse;
-import org.springframework.util.Assert;
-import org.springframework.web.context.request.RequestContextHolder;
-import org.springframework.web.context.request.ServletRequestAttributes;
-import org.springframework.web.servlet.DispatcherServlet;
-import org.springframework.web.servlet.HandlerAdapter;
-import org.springframework.web.servlet.HandlerExceptionResolver;
-import org.springframework.web.servlet.HandlerExecutionChain;
-import org.springframework.web.servlet.HandlerInterceptor;
-import org.springframework.web.servlet.HandlerMapping;
-import org.springframework.web.servlet.ModelAndView;
-import org.springframework.web.servlet.View;
-import org.springframework.web.servlet.ViewResolver;
-
-/**
- * A more "lightweight" alternative to the {@link DispatcherServlet} re-purposed for testing Spring MVC applications
- * outside of a Servlet container environment in mind. Mimics the essential functionality of the DispatcherServlet
- * but does not always behave in identical ways. For example invoking afterCompletion() on a HandlerInterceptor is
- * not essential for integration testing since the same method can be unit tested.
- *
- * Unlike the DispatcherServlet, the {@link MockDispatcher} is stateful. It records contextual information during
- * each invocation such as the request and the response, the mapped handler and handler interceptors, and the resulting
- * ModelAndView. The recorded information may then be matched against application-specific expectations as defined by
- * {@link MvcResultActions}. Previously recorded context is cleared at the start of every dispatch invocation.
- *
- * @NotThreadSafe
- */
-public class MockDispatcher {
-
- private Log logger = LogFactory.getLog(getClass());
-
- private final MvcSetup mvcSetup;
-
- private MockHttpServletRequest request;
-
- private MockHttpServletResponse response;
-
- private Object handler;
-
- private HandlerInterceptor[] interceptors;
-
- private ModelAndView mav;
-
- private Exception handlerException;
-
- /**
- * Create a {@link MockDispatcher} with the provided {@link MvcSetup}.
- */
- MockDispatcher(MvcSetup setup) {
- this.mvcSetup = setup;
- }
-
- /**
- * Process the request by invoking Spring MVC components in the {@link MvcSetup} provided to the constructor.
- * The request may be partially processed if mapOnly is {@code true}.
- *
- */
- public MvcResultActions dispatch(MockHttpServletRequest request, MockHttpServletResponse response, boolean mapOnly) {
- clear();
- this.request = request;
- this.response = response;
-
- try {
- RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request));
- doDispatch(mapOnly);
- }
- catch (Exception exception) {
- logger.error("Unhandled exception", exception);
- fail("Failed to dispatch Mock MVC request (check logs for stacktrace): " + exception);
- }
- finally {
- RequestContextHolder.resetRequestAttributes();
- }
-
- return this.new ResultActionsAdapter();
- }
-
- private void clear() {
- request = null;
- response = null;
- handler = null;
- interceptors = null;
- mav = null;
- }
-
- private void doDispatch(boolean mapOnly) throws Exception {
-
- try {
- initHandlerExecutionChain();
-
- if (handler == null || mapOnly) {
- return;
- }
-
- List interceptorList = (interceptors != null) ?
- Arrays.asList(interceptors) : new ArrayList();
-
- for (HandlerInterceptor interceptor : interceptorList) {
- if (!interceptor.preHandle(request, response, handler)) {
- return;
- }
- }
-
- HandlerAdapter adapter = getHandlerAdapter();
- mav = adapter.handle(request, response, handler);
- updateDefaultViewName();
-
- Collections.reverse(interceptorList);
- for (HandlerInterceptor interceptor : interceptorList) {
- interceptor.postHandle(request, response, handler, mav);
- }
- }
- catch (Exception exception) {
- processHandlerException(exception);
- updateDefaultViewName();
- }
-
- if (mav == null) {
- return;
- }
-
- Locale locale = mvcSetup.getLocaleResolver().resolveLocale(request);
- response.setLocale(locale);
-
- View view = resolveView(locale);
- view.render(mav.getModel(), request, response);
- }
-
- private void initHandlerExecutionChain() throws Exception {
- for (HandlerMapping mapping : mvcSetup.getHandlerMappings()) {
- HandlerExecutionChain chain = mapping.getHandler(request);
- if (chain != null) {
- handler = chain.getHandler();
- interceptors = chain.getInterceptors();
- return;
- }
- }
- response.sendError(HttpServletResponse.SC_NOT_FOUND);
- }
-
- private HandlerAdapter getHandlerAdapter() {
- for (HandlerAdapter adapter : mvcSetup.getHandlerAdapters()) {
- if (adapter.supports(handler)) {
- return adapter;
- }
- }
- throw new IllegalStateException("No adapter for handler [" + handler
- + "]. Available adapters: [" + mvcSetup.getHandlerAdapters() + "]");
- }
-
- private void updateDefaultViewName() throws Exception {
- if (mav != null && !mav.hasView()) {
- String viewName = mvcSetup.getViewNameTranslator().getViewName(request);
- mav.setViewName(viewName);
- }
- }
-
- private void processHandlerException(Exception exception) throws Exception {
- handlerException = exception;
- for (HandlerExceptionResolver resolver : mvcSetup.getExceptionResolvers()) {
- mav = resolver.resolveException(request, response, handler, exception);
- if (mav != null) {
- mav = mav.isEmpty() ? null : mav;
- return;
- }
- }
- throw exception;
- }
-
- private View resolveView(Locale locale) throws Exception {
- if (mav.isReference()) {
- for (ViewResolver viewResolver : mvcSetup.getViewResolvers()) {
- View view = viewResolver.resolveViewName(mav.getViewName(), locale);
- if (view != null) {
- return view;
- }
- }
- }
- View view = mav.getView();
- Assert.isTrue(view != null, "Could not resolve view from ModelAndView: <" + mav + ">");
- return view;
- }
-
- private class ResultActionsAdapter implements MvcResultActions {
-
- public MvcResultActions andExpect(MvcResultMatcher matcher) {
- matcher.match(request, response, handler, handlerException, mav);
- return this;
- }
-
- }
-
-}
diff --git a/src/main/java/org/springframework/test/web/server/MockFilterChain.java b/src/main/java/org/springframework/test/web/server/MockFilterChain.java
new file mode 100644
index 0000000..07927ff
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/server/MockFilterChain.java
@@ -0,0 +1,189 @@
+/*
+ * 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.server;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+import org.springframework.mock.web.MockFilterConfig;
+import org.springframework.mock.web.PassThroughFilterChain;
+import org.springframework.util.Assert;
+import org.springframework.util.ObjectUtils;
+
+/**
+ * Note:
+ * this class is a copy of the same class in the spring-test module of
+ * the Spring Framework with some additional changes. It's here temporarily
+ * until Spring MVC Test moves into the Spring Framework.
+ *
+ * Mock implementation of the {@link javax.servlet.FilterChain} interface. Used
+ * for testing the web framework; also useful for testing custom
+ * {@link javax.servlet.Filter} implementations.
+ *
+ *
A {@link MockFilterChain} can be configured with one or more filters and a
+ * Servlet to be invoked. When the chain is invoked, it invokes in turn all
+ * filters and the Servlet and saves the request and response. Subsequent
+ * invocations raise an {@link IllegalStateException} unless {@link #reset()} is
+ * called.
+ *
+ * @author Juergen Hoeller
+ * @author Rob Winch
+ * @author Rossen Stoyanchev
+ *
+ * @since 2.0.3
+ * @see MockFilterConfig
+ * @see PassThroughFilterChain
+ */
+public class MockFilterChain implements FilterChain {
+
+ private ServletRequest request;
+
+ private ServletResponse response;
+
+ private final List filters;
+
+ private Iterator iterator;
+
+
+ /**
+ * Register a single do-nothing {@link Filter} implementation. The first
+ * invocation saves the request and response. Subsequent invocations raise
+ * an {@link IllegalStateException} unless {@link #reset()} is called.
+ */
+ public MockFilterChain() {
+ this.filters = Collections.emptyList();
+ }
+
+ /**
+ * Create a FilterChain with a Servlet.
+ *
+ * @param servlet the Servlet to invoke
+ * @since 3.2
+ */
+ public MockFilterChain(Servlet servlet) {
+ this.filters = initFilterList(servlet);
+ }
+
+ /**
+ * Create a {@code FilterChain} with Filter's and a Servlet.
+ *
+ * @param servlet the {@link Servlet} to invoke in this {@link FilterChain}
+ * @param filters the {@link Filter}'s to invoke in this {@link FilterChain}
+ * @since 3.2
+ */
+ public MockFilterChain(Servlet servlet, Filter... filters) {
+ Assert.notNull(filters, "filters cannot be null");
+ Assert.noNullElements(filters, "filters cannot contain null values");
+ this.filters = initFilterList(servlet, filters);
+ }
+
+ private static List initFilterList(Servlet servlet, Filter... filters) {
+ Filter[] allFilters = ObjectUtils.addObjectToArray(filters, new ServletFilterProxy(servlet));
+ return Arrays.asList(allFilters);
+ }
+
+ /**
+ * Return the request that {@link #doFilter} has been called with.
+ */
+ public ServletRequest getRequest() {
+ return this.request;
+ }
+
+ /**
+ * Return the response that {@link #doFilter} has been called with.
+ */
+ public ServletResponse getResponse() {
+ return this.response;
+ }
+
+ /**
+ * Invoke registered {@link Filter}s and/or {@link Servlet} also saving the
+ * request and response.
+ */
+ public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
+ Assert.notNull(request, "Request must not be null");
+ Assert.notNull(response, "Response must not be null");
+
+ if (this.request != null) {
+ throw new IllegalStateException("This FilterChain has already been called!");
+ }
+
+ if (this.iterator == null) {
+ this.iterator = this.filters.iterator();
+ }
+
+ if (this.iterator.hasNext()) {
+ Filter nextFilter = this.iterator.next();
+ nextFilter.doFilter(request, response, this);
+ }
+
+ this.request = request;
+ this.response = response;
+ }
+
+ /**
+ * Reset the {@link MockFilterChain} allowing it to be invoked again.
+ */
+ public void reset() {
+ this.request = null;
+ this.response = null;
+ this.iterator = null;
+ }
+
+
+ /**
+ * A filter that simply delegates to a Servlet.
+ */
+ private static class ServletFilterProxy implements Filter {
+
+ private final Servlet delegateServlet;
+
+ private ServletFilterProxy(Servlet servlet) {
+ Assert.notNull(servlet, "servlet cannot be null");
+ this.delegateServlet = servlet;
+ }
+
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+ throws IOException, ServletException {
+
+ this.delegateServlet.service(request, response);
+ }
+
+ public void init(FilterConfig filterConfig) throws ServletException {
+ }
+
+ public void destroy() {
+ }
+
+ @Override
+ public String toString() {
+ return this.delegateServlet.toString();
+ }
+ }
+
+}
diff --git a/src/main/java/org/springframework/test/web/server/MockHttpServletRequestBuilder.java b/src/main/java/org/springframework/test/web/server/MockHttpServletRequestBuilder.java
deleted file mode 100644
index 2b0eeb8..0000000
--- a/src/main/java/org/springframework/test/web/server/MockHttpServletRequestBuilder.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package org.springframework.test.web.server;
-
-import javax.servlet.ServletContext;
-
-import org.springframework.mock.web.MockHttpServletRequest;
-
-/** @author Arjen Poutsma */
-public interface MockHttpServletRequestBuilder {
-
- MockHttpServletRequest buildRequest(ServletContext servletContext);
-
-}
diff --git a/src/main/java/org/springframework/test/web/server/MockHttpServletRequestBuilders.java b/src/main/java/org/springframework/test/web/server/MockHttpServletRequestBuilders.java
deleted file mode 100644
index 4a72fd8..0000000
--- a/src/main/java/org/springframework/test/web/server/MockHttpServletRequestBuilders.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package org.springframework.test.web.server;
-
-import java.net.URI;
-
-import org.springframework.http.HttpMethod;
-import org.springframework.web.util.UriTemplate;
-
-/** @author Arjen Poutsma */
-public abstract class MockHttpServletRequestBuilders {
-
- private MockHttpServletRequestBuilders() {
- }
-
- public static DefaultMockHttpServletRequestBuilder get(String urlTemplate, Object... urlVariables) {
- return request(HttpMethod.GET, urlTemplate, urlVariables);
- }
-
- public static DefaultMockHttpServletRequestBuilder post(String urlTemplate, Object... urlVariables) {
- return request(HttpMethod.POST, urlTemplate, urlVariables);
- }
-
- public static DefaultMockHttpServletRequestBuilder put(String urlTemplate, Object... urlVariables) {
- return request(HttpMethod.PUT, urlTemplate, urlVariables);
- }
-
- public static DefaultMockHttpServletRequestBuilder delete(String urlTemplate, Object... urlVariables) {
- return request(HttpMethod.DELETE, urlTemplate, urlVariables);
- }
-
- public static MultipartMockHttpServletRequestBuilder fileUpload(String urlTemplate, Object... urlVariables) {
- URI url = expandUrl(urlTemplate, urlVariables);
- return new MultipartMockHttpServletRequestBuilder(url);
- }
-
- public static DefaultMockHttpServletRequestBuilder request(HttpMethod method, String urlTemplate, Object... urlVariables) {
- URI url = expandUrl(urlTemplate, urlVariables);
- return new DefaultMockHttpServletRequestBuilder(url, method);
- }
-
- private static URI expandUrl(String urlTemplate, Object[] urlVariables) {
- UriTemplate uriTemplate = new UriTemplate(urlTemplate);
- return uriTemplate.expand(urlVariables);
- }
-
-
-}
diff --git a/src/main/java/org/springframework/test/web/server/MockMvc.java b/src/main/java/org/springframework/test/web/server/MockMvc.java
index 70941f7..26b30ce 100644
--- a/src/main/java/org/springframework/test/web/server/MockMvc.java
+++ b/src/main/java/org/springframework/test/web/server/MockMvc.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2011 the original author or authors.
+ * Copyright 2011-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.
@@ -16,57 +16,150 @@
package org.springframework.test.web.server;
+import java.util.ArrayList;
+import java.util.List;
+
import javax.servlet.ServletContext;
+import org.springframework.beans.Mergeable;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
+import org.springframework.util.Assert;
+
+/**
+ * Main entry point for server-side Spring MVC test support.
+ *
+ * Below is an example:
+ *
+ *
+ * static imports:
+ * MockMvcBuilders.*, MockMvcRequestBuilders.*, MockMvcResultMatchers.*
+ *
+ * MockMvc mockMvc =
+ * annotationConfigMvcSetup(TestConfiguration.class)
+ * .configureWarRootDir("src/main/webapp", false).build()
+ *
+ * mockMvc.perform(get("/form"))
+ * .andExpect(status().isOk())
+ * .andExpect(content().mimeType("text/plain"))
+ * .andExpect(forwardedUrl("/WEB-INF/layouts/main.jsp"));
+ *
+ *
+ * @author Rossen Stoyanchev
+ * @author Rob Winch
+ */
+public final class MockMvc {
+
+ static String MVC_RESULT_ATTRIBUTE = MockMvc.class.getName().concat(".MVC_RESULT_ATTRIBUTE");
+
+ private final MockFilterChain filterChain;
+
+ private final ServletContext servletContext;
+
+ private RequestBuilder defaultRequestBuilder;
+
+ private List defaultResultMatchers = new ArrayList();
+
+ private List defaultResultHandlers = new ArrayList();
+
+
+ /**
+ * Private constructor, not for direct instantiation.
+ * @see org.springframework.test.web.server.setup.MockMvcBuilders
+ */
+ MockMvc(MockFilterChain filterChain, ServletContext servletContext) {
+ Assert.notNull(servletContext, "A ServletContext is required");
+ Assert.notNull(filterChain, "A MockFilterChain is required");
+
+ this.filterChain = filterChain;
+ this.servletContext = servletContext;
+ }
+
+ /**
+ * A default request builder merged into every performed request.
+ * @see org.springframework.test.web.server.setup.AbstractMockMvcBuilder#defaultRequest(RequestBuilder)
+ */
+ void setDefaultRequest(RequestBuilder requestBuilder) {
+ this.defaultRequestBuilder = requestBuilder;
+ }
+
+ /**
+ * Expectations to assert after every performed request.
+ * @see org.springframework.test.web.server.setup.AbstractMockMvcBuilder#alwaysExpect(ResultMatcher)
+ */
+ void setGlobalResultMatchers(List resultMatchers) {
+ Assert.notNull(resultMatchers, "resultMatchers is required");
+ this.defaultResultMatchers = resultMatchers;
+ }
+
+ /**
+ * General actions to apply after every performed request.
+ * @see org.springframework.test.web.server.setup.AbstractMockMvcBuilder#alwaysDo(ResultHandler)
+ */
+ void setGlobalResultHandlers(List 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.server.request.MockMvcRequestBuilders}
+ *
+ * @return an instance of {@link ResultActions}; never {@code null}
+ *
+ * @see org.springframework.test.web.server.request.MockMvcRequestBuilders
+ * @see org.springframework.test.web.server.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);
+
+ this.filterChain.reset();
+ this.filterChain.doFilter(request, response);
+
+ applyDefaultResultActions(mvcResult);
+
+ return new ResultActions() {
+
+ public ResultActions andExpect(ResultMatcher matcher) throws Exception {
+ matcher.match(mvcResult);
+ return this;
+ }
+
+ public ResultActions andDo(ResultHandler printer) throws Exception {
+ printer.handle(mvcResult);
+ return this;
+ }
+
+ public MvcResult andReturn() {
+ return mvcResult;
+ }
+ };
+ }
+
+ private void applyDefaultResultActions(MvcResult mvcResult) throws Exception {
+
+ for (ResultMatcher matcher : this.defaultResultMatchers) {
+ matcher.match(mvcResult);
+ }
-/** Main entry point for server-side Spring MVC test support. */
-public class MockMvc {
-
- private final ServletContext servletContext;
-
- private final MockDispatcher mockDispatcher;
-
- private boolean mapOnly;
-
- /** To create a {@link MockMvc} instance see methods in {@code MockMvcBuilders}. */
- MockMvc(ServletContext servletContext, MockDispatcher mockDispatcher) {
- this.servletContext = servletContext;
- this.mockDispatcher = mockDispatcher;
- }
-
- /**
- * Enables a mode in which requests are mapped to a handler without actually invoking it afterwards. Allows verifying
- * the handler or handler method a request is mapped to.
- */
- public MockMvc setMapOnly(boolean enable) {
- this.mapOnly = enable;
- return this;
- }
-
- /*
- public static MockMvc createFromApplicationContext(ApplicationContext applicationContext) {
- // TODO
- return null;
- }
-
- public static MockMvc createFromWebXml(String webXmlFileName) {
- // TODO
- return null;
- }
- */
-
- // Perform
-
- public MvcResultActions perform(MockHttpServletRequestBuilder requestBuilder) {
- MockHttpServletRequest request = requestBuilder.buildRequest(servletContext);
- MockHttpServletResponse response = new MockHttpServletResponse();
- return execute(request, response);
- }
-
- protected MvcResultActions execute(MockHttpServletRequest request, MockHttpServletResponse response) {
- return mockDispatcher.dispatch(request, response, mapOnly);
- }
+ for (ResultHandler handler : this.defaultResultHandlers) {
+ handler.handle(mvcResult);
+ }
+ }
}
diff --git a/src/main/java/org/springframework/test/web/server/MvcResultActions.java b/src/main/java/org/springframework/test/web/server/MockMvcBuilder.java
similarity index 60%
rename from src/main/java/org/springframework/test/web/server/MvcResultActions.java
rename to src/main/java/org/springframework/test/web/server/MockMvcBuilder.java
index 817445e..fe3826a 100644
--- a/src/main/java/org/springframework/test/web/server/MvcResultActions.java
+++ b/src/main/java/org/springframework/test/web/server/MockMvcBuilder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2011 the original author or authors.
+ * Copyright 2011 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.
@@ -16,17 +16,19 @@
package org.springframework.test.web.server;
-
-
/**
- * Allows setting up actions against the results of a processed request via method chaining.
+ * Builds a {@link MockMvc}.
+ *
+ * See static, factory methods in
+ * {@code org.springframework.test.web.server.setup.MockMvcBuilders}.
+ *
+ * @author Rossen Stoyanchev
*/
-public interface MvcResultActions {
+public interface MockMvcBuilder {
/**
- * Define an expectation about the results from a processed request.
- * See methods in {@code MvcResultMatchers} for most commonly used {@link MvcResultMatcher}s.
+ * Build a {@link MockMvc} instance.
*/
- MvcResultActions andExpect(MvcResultMatcher matcher);
+ MockMvc build();
}
\ No newline at end of file
diff --git a/src/main/java/org/springframework/test/web/server/MockMvcBuilderSupport.java b/src/main/java/org/springframework/test/web/server/MockMvcBuilderSupport.java
new file mode 100644
index 0000000..db35180
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/server/MockMvcBuilderSupport.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2011-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.server;
+
+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.
+ *
+ *
{@link org.springframework.test.web.server.setup.AbstractMockMvcBuilder},
+ * 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
+ */
+public abstract class MockMvcBuilderSupport {
+
+ protected MockMvc createMockMvc(Filter[] filters, MockServletConfig servletConfig,
+ WebApplicationContext webAppContext, RequestBuilder defaultRequestBuilder,
+ List globalResultMatchers, List globalResultHandlers) {
+
+ ServletContext servletContext = webAppContext.getServletContext();
+
+ TestDispatcherServlet dispatcherServlet = new TestDispatcherServlet(webAppContext);
+ try {
+ dispatcherServlet.init(servletConfig);
+ }
+ catch (ServletException ex) {
+ // should never happen..
+ throw new MockMvcBuildException("Failed to initialize TestDispatcherServlet", ex);
+ }
+
+ MockFilterChain filterChain = new MockFilterChain(dispatcherServlet, filters);
+
+ MockMvc mockMvc = new MockMvc(filterChain, 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);
+ }
+ }
+
+}
diff --git a/src/main/java/org/springframework/test/web/server/MultipartMockHttpServletRequestBuilder.java b/src/main/java/org/springframework/test/web/server/MultipartMockHttpServletRequestBuilder.java
deleted file mode 100644
index d5e4ad3..0000000
--- a/src/main/java/org/springframework/test/web/server/MultipartMockHttpServletRequestBuilder.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright 2002-2011 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.server;
-
-import java.net.URI;
-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;
-
-/**
- * Implementation of the {@link MockHttpServletRequestBuilder} interface that provides access to multipart requests.
- *
- */
-public class MultipartMockHttpServletRequestBuilder extends DefaultMockHttpServletRequestBuilder {
-
- private final List files = new ArrayList();
-
- MultipartMockHttpServletRequestBuilder(URI uri) {
- super(uri, HttpMethod.POST);
- 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 MultipartMockHttpServletRequestBuilder file(String name, byte[] content) {
- files.add(new MockMultipartFile(name, content));
- return this;
- }
-
- /**
- * Adds the given MockMultipartFile.
- *
- * @param file the multipart file
- */
- public MultipartMockHttpServletRequestBuilder file(MockMultipartFile file) {
- files.add(file);
- return this;
- }
-
- @Override
- protected final MockHttpServletRequest createServletRequest(ServletContext servletContext) {
- MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest();
- for (MockMultipartFile file : files) {
- request.addFile(file);
- }
- return request;
- }
-
-}
diff --git a/src/main/java/org/springframework/test/web/server/MvcResult.java b/src/main/java/org/springframework/test/web/server/MvcResult.java
new file mode 100644
index 0000000..83db98f
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/server/MvcResult.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2011-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.server;
+
+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
+ */
+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();
+
+}
diff --git a/src/main/java/org/springframework/test/web/server/MvcSetup.java b/src/main/java/org/springframework/test/web/server/MvcSetup.java
deleted file mode 100644
index 18afc9f..0000000
--- a/src/main/java/org/springframework/test/web/server/MvcSetup.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2002-2011 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.server;
-
-import java.util.List;
-
-import org.springframework.web.servlet.HandlerAdapter;
-import org.springframework.web.servlet.HandlerExceptionResolver;
-import org.springframework.web.servlet.HandlerMapping;
-import org.springframework.web.servlet.LocaleResolver;
-import org.springframework.web.servlet.RequestToViewNameTranslator;
-import org.springframework.web.servlet.ViewResolver;
-
-/**
- * Provides access to Spring MVC infrastructure components.
- *
- */
-public interface MvcSetup {
-
- List getHandlerMappings();
-
- List getHandlerAdapters();
-
- List getExceptionResolvers();
-
- List getViewResolvers();
-
- RequestToViewNameTranslator getViewNameTranslator();
-
- LocaleResolver getLocaleResolver();
-
-}
diff --git a/src/main/java/org/springframework/test/web/server/RequestBuilder.java b/src/main/java/org/springframework/test/web/server/RequestBuilder.java
new file mode 100644
index 0000000..7a48755
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/server/RequestBuilder.java
@@ -0,0 +1,26 @@
+package org.springframework.test.web.server;
+
+import javax.servlet.ServletContext;
+
+import org.springframework.mock.web.MockHttpServletRequest;
+
+/**
+ * Builds a {@link MockHttpServletRequest}.
+ *
+ * See static, factory methods in
+ * {@code org.springframework.test.web.server.request.MockMvcRequestBuilders}.
+ *
+ * @author Arjen Poutsma
+ * @author Rossen Stoyanchev
+ */
+public interface RequestBuilder {
+
+ /**
+ * Build the request.
+ *
+ * @param servletContext the {@link ServletContext} to use to create the request
+ * @return the request
+ */
+ MockHttpServletRequest buildRequest(ServletContext servletContext);
+
+}
diff --git a/src/main/java/org/springframework/test/web/server/ResultActions.java b/src/main/java/org/springframework/test/web/server/ResultActions.java
new file mode 100644
index 0000000..dc47614
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/server/ResultActions.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2011-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.server;
+
+/**
+ * Allows applying actions, such as expectations, on the result of an executed
+ * request.
+ *
+ *
See static factory methods in
+ * {@code org.springframework.test.web.server.result.MockMvcResultMatchers}
+ * {@code org.springframework.test.web.server.result.MockMvcResultHandlers}
+ *
+ * @author Rossen Stoyanchev
+ */
+public interface ResultActions {
+
+ /**
+ * Provide an expectation. For example:
+ *
+ * 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!"));
+ *
+ */
+ ResultActions andExpect(ResultMatcher matcher) throws Exception;
+
+ /**
+ * Provide a general action. For example:
+ *
+ * static imports: MockMvcRequestBuilders.*, MockMvcResultMatchers.*
+ *
+ * mockMvc.perform(get("/form")).andDo(print());
+ *
+ */
+ 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();
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/springframework/test/web/server/ResultHandler.java b/src/main/java/org/springframework/test/web/server/ResultHandler.java
new file mode 100644
index 0000000..96a1702
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/server/ResultHandler.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2011 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.server;
+
+/**
+ * Executes a generic action (e.g. printing debug information) on the result of
+ * an executed request.
+ *
+ * See static factory methods in
+ * {@code org.springframework.test.web.server.result.MockMvcResultHandlers}.
+ *
+ *
Example:
+ *
+ *
+ * static imports: MockMvcRequestBuilders.*, MockMvcResultHandlers.*
+ *
+ * mockMvc.perform(get("/form")).andDo(print());
+ *
+ *
+ * @author Rossen Stoyanchev
+ */
+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;
+
+}
diff --git a/src/main/java/org/springframework/test/web/server/ResultMatcher.java b/src/main/java/org/springframework/test/web/server/ResultMatcher.java
new file mode 100644
index 0000000..6559546
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/server/ResultMatcher.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2011-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.server;
+
+/**
+ * Matches the result of an executed request against some expectation.
+ *
+ * See static factory methods in
+ * {@code org.springframework.test.web.server.result.MockMvcResultMatchers}.
+ *
+ *
Example:
+ *
+ *
+ * static imports: MockMvcRequestBuilders.*, MockMvcResultMatchers.*
+ *
+ * mockMvc.perform(get("/form"))
+ * .andExpect(status().isOk())
+ * .andExpect(content().mimeType(MediaType.APPLICATION_JSON));
+ *
+ *
+ * @author Rossen Stoyanchev
+ */
+public interface ResultMatcher {
+
+ /**
+ * Assert the result of an executed request.
+ *
+ * @param mvcResult the result of the executed request
+ * @throws Exception if a failure occurs
+ */
+ void match(MvcResult result) throws Exception;
+
+}
diff --git a/src/main/java/org/springframework/test/web/server/TestDispatcherServlet.java b/src/main/java/org/springframework/test/web/server/TestDispatcherServlet.java
new file mode 100644
index 0000000..5bcfef1
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/server/TestDispatcherServlet.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2011-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.server;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.web.context.WebApplicationContext;
+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
+ */
+@SuppressWarnings("serial")
+final class TestDispatcherServlet extends DispatcherServlet {
+
+ /**
+ * Create a new instance with the given web application context.
+ */
+ public TestDispatcherServlet(WebApplicationContext webApplicationContext) {
+ super(webApplicationContext);
+ }
+
+ 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;
+ }
+
+}
diff --git a/src/main/java/org/springframework/test/web/server/matcher/HandlerMatchers.java b/src/main/java/org/springframework/test/web/server/matcher/HandlerMatchers.java
deleted file mode 100644
index e8b8412..0000000
--- a/src/main/java/org/springframework/test/web/server/matcher/HandlerMatchers.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright 2002-2011 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.server.matcher;
-
-import static org.springframework.test.web.AssertionErrors.assertEquals;
-import static org.springframework.test.web.AssertionErrors.assertTrue;
-
-import java.lang.reflect.Method;
-
-import org.springframework.mock.web.MockHttpServletRequest;
-import org.springframework.mock.web.MockHttpServletResponse;
-import org.springframework.test.web.server.MvcResultMatcher;
-import org.springframework.util.ReflectionUtils;
-import org.springframework.web.method.HandlerMethod;
-import org.springframework.web.servlet.ModelAndView;
-
-/**
- * Matchers for applying assertions on the handler matched to a request.
- *
- */
-public abstract class HandlerMatchers {
-
- private HandlerMatchers() {
- }
-
- public static MvcResultMatcher handlerMethod(final String methodName) {
- return new HandlerMethodResultMatcher() {
- protected void matchHandlerMethod(HandlerMethod handlerMethod) {
- assertEquals("Method", methodName, handlerMethod.getMethod().getName());
- }
- };
- }
-
- public static MvcResultMatcher handlerMethod(final Class> controllerType,
- final String methodName,
- final Class>...argumentTypes) {
- return new HandlerMethodResultMatcher() {
- protected void matchHandlerMethod(HandlerMethod handlerMethod) {
- Method method = ReflectionUtils.findMethod(controllerType, methodName, argumentTypes);
- assertTrue("Method not found", method != null);
- assertEquals("Method", method, handlerMethod.getMethod());
- }
- };
- }
-
- public static MvcResultMatcher handlerType(final Class> handlerType) {
- return new HandlerResultMatcher() {
- protected void matchHandler(Object handler) {
- assertEquals("Handler type", handlerType, handler.getClass());
- }
- };
- }
-
- private abstract static class HandlerResultMatcher implements MvcResultMatcher {
-
- public final void match(MockHttpServletRequest request,
- MockHttpServletResponse response,
- Object handler,
- Exception handlerException,
- ModelAndView mav) {
- assertTrue("No matching handler", handler != null);
- matchHandler(handler);
- }
-
- protected abstract void matchHandler(Object handler);
- }
-
- private abstract static class HandlerMethodResultMatcher extends HandlerResultMatcher {
-
- @Override
- protected void matchHandler(Object handler) {
- Class> type = handler.getClass();
- assertTrue("Expected HandlerMethod. Actual type " + type, HandlerMethod.class.isAssignableFrom(type));
- matchHandlerMethod((HandlerMethod) handler);
- }
-
- protected abstract void matchHandlerMethod(HandlerMethod handlerMethod);
- }
-
-}
diff --git a/src/main/java/org/springframework/test/web/server/matcher/LoggingMatcher.java b/src/main/java/org/springframework/test/web/server/matcher/LoggingMatcher.java
deleted file mode 100644
index fd43390..0000000
--- a/src/main/java/org/springframework/test/web/server/matcher/LoggingMatcher.java
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * Copyright 2002-2011 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.server.matcher;
-
-import java.io.UnsupportedEncodingException;
-import java.util.Map;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.springframework.mock.web.MockHttpServletRequest;
-import org.springframework.mock.web.MockHttpServletResponse;
-import org.springframework.test.web.server.MvcResultMatcher;
-import org.springframework.util.Assert;
-import org.springframework.validation.BindingResult;
-import org.springframework.validation.Errors;
-import org.springframework.web.method.HandlerMethod;
-import org.springframework.web.servlet.ModelAndView;
-
-public class LoggingMatcher implements MvcResultMatcher {
-
- private static final Log logger = LogFactory.getLog(LoggingMatcher.class);
-
- public void match(MockHttpServletRequest request,
- MockHttpServletResponse response,
- Object handler,
- Exception handlerException,
- ModelAndView mav) {
-
- StringBuilder sb = new StringBuilder();
-
- appendRequest(sb, request);
- appendHandler(sb, handler, handlerException);
- appendModelAndView(sb, mav);
- appendResponse(sb, response);
-
- logger.info(sb.toString());
- }
-
- private void appendRequest(StringBuilder sb, MockHttpServletRequest request) {
- sb.append("\n\n" + request.getMethod() + " " + request.getRequestURI() + "\n");
- appendLabelAndValue(sb, "Params", request.getParameterMap());
- appendLabelAndValue(sb, "Headers", MockRequestMatchers.getHeaderValueMap(request));
- }
-
- private void appendHandler(StringBuilder sb, Object handler, Exception handlerException) {
- if (handler == null) {
- sb.append("\nSelected Handler: null\n");
- return;
- }
-
- sb.append("\nSelected Handler:\n");
- if (!HandlerMethod.class.isInstance(handler)) {
- appendLabelAndValue(sb, "Type", handler.getClass().getName());
- appendLabelAndValue(sb, "Method", "Not available");
- }
- else {
- HandlerMethod hm = (HandlerMethod) handler;
- appendLabelAndValue(sb, "Type", hm.getBeanType().getName());
- appendLabel(sb, "Method");
-
- sb.append(hm.getReturnType().getParameterType().getSimpleName());
- sb.append(" " + hm.getMethod().getName() + "(");
- for (int i = 0; i < hm.getMethod().getParameterTypes().length; i++) {
- if (i != 0) {
- sb.append(", ");
- }
- sb.append(hm.getMethod().getParameterTypes()[i].getSimpleName());
- }
- sb.append(") \n");
- }
-
- if (handlerException == null) {
- sb.append("\nHandler Exception Raised: none\n");
- }
- else {
- sb.append("\nHandler Exception Raised:\n" + handlerException + "\n");
- }
- }
-
- private void appendLabel(StringBuilder sb, String label) {
- for (int i = 0; i < (17 - label.length()); i++) {
- sb.append(" ");
- }
- sb.append(label + ": ");
- }
-
- private void appendLabelAndValue(StringBuilder sb, String label, Object value) {
- appendLabel(sb, label);
- sb.append(value + "\n");
- }
-
- private void appendModelAndView(StringBuilder sb, ModelAndView mav) {
- sb.append("\nModelAndView: ");
- if (mav != null) {
- sb.append("\n");
- appendView(sb, mav);
- appendModel(sb, mav.getModel());
- }
- else {
- sb.append("null\n");
- }
- }
-
- private void appendView(StringBuilder sb, ModelAndView mav) {
- Assert.notNull(mav);
- if (mav.isReference()) {
- appendLabelAndValue(sb, "View name", "\"" + mav.getViewName() + "\"");
- }
- else {
- appendLabelAndValue(sb, "View", mav.getView());
- }
- }
-
- private void appendModel(StringBuilder sb, Map model) {
- if (model.size() == 0) {
- appendLabelAndValue(sb, "Attributes", "none");
- sb.append("none");
- return;
- }
- for (String name : model.keySet()) {
- if (!name.startsWith(BindingResult.MODEL_KEY_PREFIX)) {
- Object value = model.get(name);
- Errors errors = (Errors) model.get(BindingResult.MODEL_KEY_PREFIX + name);
- if (errors == null) {
- appendLabelAndValue(sb, "Attribute", name);
- }
- else {
- appendLabelAndValue(sb, "Attribute", name + " has " + errors.getErrorCount() + " errors");
- }
- if (logger.isTraceEnabled()) {
- appendLabelAndValue(sb, "value", value);
- if (errors != null) {
- appendLabelAndValue(sb, "errors", errors.getAllErrors());
- }
- }
- }
- }
- }
-
- private void appendResponse(StringBuilder sb, MockHttpServletResponse response) {
- sb.append("\nResponse:\n");
- appendLabelAndValue(sb, "status", response.getStatus());
- appendLabelAndValue(sb, "error message", response.getErrorMessage());
- appendLabelAndValue(sb, "headers", MockResponseMatchers.getHeaderValueMap(response));
- appendLabelAndValue(sb, "content type", response.getContentType());
- appendResponseBody(sb, response);
- appendLabelAndValue(sb, "forwarded URL", response.getForwardedUrl());
- appendLabelAndValue(sb, "redirected URL", response.getRedirectedUrl());
- appendLabelAndValue(sb, "included URLs", response.getIncludedUrls());
- appendLabelAndValue(sb, "cookies", MockResponseMatchers.getCookieValueMap(response));
- sb.append("\n");
- }
-
- private void appendResponseBody(StringBuilder sb, MockHttpServletResponse response) {
- String content;
- try {
- content = response.getContentAsString();
-
- } catch (UnsupportedEncodingException e) {
- String message = "Failed to get the response content: ";
- content = message + e.toString();
- logger.error(message, e);
- }
- if (content != null) {
- int length = content.length();
- if (length > 50) {
- content = content.substring(0, 50);
- appendLabelAndValue(sb, "response body", "[" + content + "] (50 of " + " " + length + " chars)");
- }
- else {
- appendLabelAndValue(sb, "response body", "[" + content + "]");
- }
- }
- }
-
-}
diff --git a/src/main/java/org/springframework/test/web/server/matcher/MockRequestMatchers.java b/src/main/java/org/springframework/test/web/server/matcher/MockRequestMatchers.java
deleted file mode 100644
index 27faf52..0000000
--- a/src/main/java/org/springframework/test/web/server/matcher/MockRequestMatchers.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright 2002-2011 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.server.matcher;
-
-import static org.springframework.test.web.AssertionErrors.assertEquals;
-
-import java.util.Enumeration;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-import javax.servlet.ServletRequest;
-import javax.servlet.http.HttpSession;
-
-import org.springframework.mock.web.MockHttpServletRequest;
-import org.springframework.mock.web.MockHttpServletResponse;
-import org.springframework.test.web.AssertionErrors;
-import org.springframework.test.web.server.MvcResultMatcher;
-import org.springframework.web.servlet.ModelAndView;
-
-/**
- * Matchers assertions on a {@link MockHttpServletRequest}.
- *
- */
-public abstract class MockRequestMatchers {
-
- private MockRequestMatchers() {
- }
-
- public static MvcResultMatcher requestAttributeValue(final String name, final Object value) {
- return new MockRequestResultMatcher() {
- protected void matchMockRequest(MockHttpServletRequest request) {
- assertEquals("Request attribute", value, request.getAttribute(name));
- }
- };
- }
-
- public static MvcResultMatcher requestAttributesPresent(final String...names) {
- return new MockRequestResultMatcher() {
- protected void matchMockRequest(MockHttpServletRequest request) {
- AssertionErrors.assertNameValuesPresent("Request attribute", getRequestAttributeMap(request), names);
- }
- };
- }
-
- public static MvcResultMatcher requestAttributesNotPresent(final String...names) {
- return new MockRequestResultMatcher() {
- protected void matchMockRequest(MockHttpServletRequest request) {
- AssertionErrors.assertNameValuesNotPresent("Request attribute", getRequestAttributeMap(request), names);
- }
- };
- }
-
- public static MvcResultMatcher sessionAttributeValue(final String name, final Object value) {
- return new MockRequestResultMatcher() {
- protected void matchMockRequest(MockHttpServletRequest request) {
- assertEquals("Session attribute", value, request.getSession().getAttribute(name));
- }
- };
- }
-
- public static MvcResultMatcher sessionAttributesPresent(final String...names) {
- return new MockRequestResultMatcher() {
- protected void matchMockRequest(MockHttpServletRequest request) {
- HttpSession session = request.getSession();
- AssertionErrors.assertNameValuesPresent("Session attribute", getSessionAttributeMap(session), names);
- }
- };
- }
-
- public static MvcResultMatcher sessionAttributesNotPresent(final String...names) {
- return new MockRequestResultMatcher() {
- protected void matchMockRequest(MockHttpServletRequest request) {
- HttpSession session = request.getSession();
- AssertionErrors.assertNameValuesNotPresent("Session attribute", getSessionAttributeMap(session), names);
- }
- };
- }
-
- static Map getHeaderValueMap(MockHttpServletRequest request) {
- Map map = new LinkedHashMap();
- Enumeration> names = request.getHeaderNames();
- while (names.hasMoreElements()) {
- String name = (String) names.nextElement();
- map.put(name, request.getHeader(name));
- }
- return map;
- }
-
- static Map getRequestAttributeMap(ServletRequest request) {
- Map map = new LinkedHashMap();
- Enumeration> names = request.getAttributeNames();
- while (names.hasMoreElements()) {
- String name = (String) names.nextElement();
- map.put(name, request.getAttribute(name));
- }
- return map;
- }
-
- static Map getSessionAttributeMap(HttpSession session) {
- Map map = new LinkedHashMap();
- Enumeration> names = session.getAttributeNames();
- while (names.hasMoreElements()) {
- String name = (String) names.nextElement();
- map.put(name, session.getAttribute(name));
- }
-
- return map;
- }
-
- private abstract static class MockRequestResultMatcher implements MvcResultMatcher {
-
- public final void match(MockHttpServletRequest request,
- MockHttpServletResponse response,
- Object handler,
- Exception handlerException,
- ModelAndView mav) {
- matchMockRequest(request);
- }
-
- protected abstract void matchMockRequest(MockHttpServletRequest request);
- }
-
-}
diff --git a/src/main/java/org/springframework/test/web/server/matcher/MockResponseMatchers.java b/src/main/java/org/springframework/test/web/server/matcher/MockResponseMatchers.java
deleted file mode 100644
index 2f41a30..0000000
--- a/src/main/java/org/springframework/test/web/server/matcher/MockResponseMatchers.java
+++ /dev/null
@@ -1,226 +0,0 @@
-/*
- * Copyright 2002-2011 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.server.matcher;
-
-import static org.springframework.test.web.AssertionErrors.assertEquals;
-import static org.springframework.test.web.AssertionErrors.assertTrue;
-import static org.springframework.test.web.AssertionErrors.fail;
-
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-import javax.servlet.http.Cookie;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.springframework.mock.web.MockHttpServletRequest;
-import org.springframework.mock.web.MockHttpServletResponse;
-import org.springframework.test.web.AssertionErrors;
-import org.springframework.test.web.server.MvcResultMatcher;
-import org.springframework.util.StringUtils;
-import org.springframework.web.servlet.ModelAndView;
-
-/**
- * Matchers for assertions on a {@link MockHttpServletResponse}.
- *
- */
-public abstract class MockResponseMatchers {
-
- private static final Log logger = LogFactory.getLog(MockResponseMatchers.class);
-
- private MockResponseMatchers() {
- }
-
- public static MvcResultMatcher status(final int status) {
- return new MockResponseResultMatcher() {
- protected void matchMockResponse(MockHttpServletResponse response) {
- assertEquals("Status", status, response.getStatus());
- }
- };
- }
-
- public static MvcResultMatcher errorMessage(final String errorMessage) {
- return new MockResponseResultMatcher() {
- protected void matchMockResponse(MockHttpServletResponse response) {
- assertEquals("Error message", errorMessage, response.getErrorMessage());
- }
- };
- }
-
- public static MvcResultMatcher contentType(final String contentType) {
- return new MvcResultMatcher() {
- public void match(MockHttpServletRequest rq, MockHttpServletResponse response, Object h, Exception e, ModelAndView mav) {
- if (StringUtils.hasText(response.getContentType())) {
- assertEquals("Content type", contentType, response.getContentType());
- }
- else {
- String headerName = "Content-Type";
- assertEquals("Content-Type response header", contentType, response.getHeader(headerName));
- }
- }
- };
- }
-
- public static MvcResultMatcher responseBody(final String content) {
- return new MockResponseResultMatcher() {
- protected void matchMockResponse(MockHttpServletResponse response) throws UnsupportedEncodingException {
- assertEquals("Response body", content, response.getContentAsString());
- }
- };
- }
-
- public static MvcResultMatcher responseBodyContains(final String text) {
- return new MockResponseResultMatcher() {
- protected void matchMockResponse(MockHttpServletResponse response) throws UnsupportedEncodingException {
- String body = response.getContentAsString();
- assertTrue("Response body <" + body + "> does not contain " + text, body.contains(text));
- }
- };
- }
-
- public static MvcResultMatcher responseBodyAsByteArray(final byte[] content) {
- return new MockResponseResultMatcher() {
- protected void matchMockResponse(MockHttpServletResponse response) {
- assertEquals("Response body", content, response.getContentAsByteArray());
- }
- };
- }
-
- public static MvcResultMatcher forwardedUrl(final String forwardUrl) {
- return new MockResponseResultMatcher() {
- protected void matchMockResponse(MockHttpServletResponse response) {
- assertEquals("Forwarded URL", forwardUrl, response.getForwardedUrl());
- }
- };
- }
-
- public static MvcResultMatcher redirectedUrl(final String redirectUrl) {
- return new MockResponseResultMatcher() {
- protected void matchMockResponse(MockHttpServletResponse response) {
- assertEquals("Redirected URL", redirectUrl, response.getRedirectedUrl());
- }
- };
- }
-
- public static MvcResultMatcher headersPresent(final String...headerNames) {
- return new MockResponseResultMatcher() {
- protected void matchMockResponse(MockHttpServletResponse response) {
- AssertionErrors.assertNameValuesPresent("Response header", getHeaderValueMap(response), headerNames);
- }
- };
- }
-
- public static MvcResultMatcher headersNotPresent(final String...headerNames) {
- return new MockResponseResultMatcher() {
- protected void matchMockResponse(MockHttpServletResponse response) {
- AssertionErrors.assertNameValuesNotPresent("Response header", getHeaderValueMap(response), headerNames);
- }
- };
- }
-
- public static MvcResultMatcher headerValue(final String headerName, final Object headerValue) {
- return new MockResponseResultMatcher() {
- protected void matchMockResponse(MockHttpServletResponse response) {
- assertEquals("Response header", headerValue, response.getHeader(headerName));
- }
- };
- }
-
- public static MvcResultMatcher headerValueContains(final String headerName, final String text) {
- return new MockResponseResultMatcher() {
- protected void matchMockResponse(MockHttpServletResponse response) {
- AssertionErrors.assertNameValuesPresent("Response header", getHeaderValueMap(response), headerName);
- Object value = response.getHeader(headerName);
- assertEquals("Header value type", String.class, response.getHeader(headerName).getClass());
- assertTrue("Header '" + headerName + "' with value <" + value + "> does not contain <" + text + ">.",
- ((String) value).contains(text));
- }
- };
- }
-
- public static MvcResultMatcher cookiesPresent(final String...names) {
- return new MockResponseResultMatcher() {
- protected void matchMockResponse(MockHttpServletResponse response) {
- AssertionErrors.assertNameValuesPresent("Response cookie", getCookieValueMap(response), names);
- }
- };
- }
-
- public static MvcResultMatcher cookiesNotPresent(final String...names) {
- return new MockResponseResultMatcher() {
- protected void matchMockResponse(MockHttpServletResponse response) {
- AssertionErrors.assertNameValuesNotPresent("Response cookie", getCookieValueMap(response), names);
- }
- };
- }
-
- public static MvcResultMatcher cookieValue(final String name, final String value) {
- return new MockResponseResultMatcher() {
- protected void matchMockResponse(MockHttpServletResponse response) {
- assertEquals("Response cookie", value, response.getCookie(name).getValue());
- }
- };
- }
-
- public static MvcResultMatcher cookieValueContains(final String cookieName, final String text) {
- return new MockResponseResultMatcher() {
- protected void matchMockResponse(MockHttpServletResponse response) {
- AssertionErrors.assertNameValuesPresent("Response cookie", getCookieValueMap(response), cookieName);
- String value = response.getCookie(cookieName).getValue();
- assertTrue("Cookie '" + cookieName + "' with value <" + value + "> does not contain <" + text + ">.",
- value.contains(text));
- }
- };
- }
-
- static Map getHeaderValueMap(MockHttpServletResponse response) {
- Map headers = new LinkedHashMap();
- for (String name : response.getHeaderNames()) {
- headers.put(name, response.getHeader(name));
- }
- return headers;
- }
-
- static Map getCookieValueMap(MockHttpServletResponse response) {
- Map cookies = new LinkedHashMap();
- for (Cookie cookie : response.getCookies()) {
- cookies.put(cookie.getName(), cookie.getValue());
- }
- return cookies;
- }
-
- private static abstract class MockResponseResultMatcher implements MvcResultMatcher {
-
- public void match(MockHttpServletRequest request,
- MockHttpServletResponse response,
- Object handler,
- Exception handlerException,
- ModelAndView mav) {
- try {
- matchMockResponse(response);
- } catch (IOException e) {
- logger.error(e.getMessage(), e);
- fail("Failed mock response expectation: " + e.getMessage());
- }
- }
-
- protected abstract void matchMockResponse(MockHttpServletResponse response) throws IOException;
- }
-
-}
diff --git a/src/main/java/org/springframework/test/web/server/matcher/ModelAndViewMatchers.java b/src/main/java/org/springframework/test/web/server/matcher/ModelAndViewMatchers.java
deleted file mode 100644
index 51a0e84..0000000
--- a/src/main/java/org/springframework/test/web/server/matcher/ModelAndViewMatchers.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright 2002-2011 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.server.matcher;
-
-import static org.springframework.test.web.AssertionErrors.assertEquals;
-import static org.springframework.test.web.AssertionErrors.assertTrue;
-import static org.springframework.test.web.AssertionErrors.fail;
-
-import org.springframework.mock.web.MockHttpServletRequest;
-import org.springframework.mock.web.MockHttpServletResponse;
-import org.springframework.test.web.AssertionErrors;
-import org.springframework.test.web.server.MvcResultMatcher;
-import org.springframework.validation.BindingResult;
-import org.springframework.web.servlet.ModelAndView;
-
-/**
- * Matchers for assertions on a {@link ModelAndView}.
- *
- */
-public abstract class ModelAndViewMatchers {
-
- private ModelAndViewMatchers() {
- }
-
- public static MvcResultMatcher modelAttribute(final String name, final Object value) {
- return new ModelAndViewResultMatcher() {
- protected void matchModelAndView(ModelAndView mav) {
- assertEquals("Model attribute", value, mav.getModel().get(name));
- }
- };
- }
-
- public static MvcResultMatcher modelAttributesPresent(final String...names) {
- return new ModelAndViewResultMatcher() {
- protected void matchModelAndView(ModelAndView mav) {
- AssertionErrors.assertNameValuesPresent("Model attribute", mav.getModelMap(), names);
- }
- };
- }
-
- public static MvcResultMatcher modelAttributesNotPresent(final String...names) {
- return new ModelAndViewResultMatcher() {
- protected void matchModelAndView(ModelAndView mav) {
- AssertionErrors.assertNameValuesNotPresent("Model attribute", mav.getModelMap(), names);
- }
- };
- }
-
- public static MvcResultMatcher noBindingErrors() {
- return new ModelAndViewResultMatcher() {
- protected void matchModelAndView(ModelAndView mav) {
- for (String name : mav.getModel().keySet()) {
- if (!name.startsWith(BindingResult.MODEL_KEY_PREFIX)) {
- continue;
- }
- BindingResult result = (BindingResult) mav.getModel().get(name);
- if (result.hasErrors()) {
- fail("Model attribute <" + name + "> has binding errors: " + result);
- }
- }
- }
- };
- }
-
- public static MvcResultMatcher modelAttributesWithNoErrors(final String...names) {
- return new ModelAndViewResultMatcher() {
- protected void matchModelAndView(ModelAndView mav) {
- AssertionErrors.assertNameValuesPresent("Model attribute", mav.getModelMap(), names);
- for (String name : names) {
- BindingResult result = (BindingResult) mav.getModel().get(BindingResult.MODEL_KEY_PREFIX + name);
- if (result.hasErrors()) {
- fail("Expected no bind errors for model attribute <" + name + "> but got " + result);
- }
- }
- }
- };
- }
-
- public static MvcResultMatcher modelAttributesWithErrors(final String...names) {
- return new ModelAndViewResultMatcher() {
- protected void matchModelAndView(ModelAndView mav) {
- AssertionErrors.assertNameValuesPresent("Model attribute", mav.getModelMap(), names);
- for (String name : names) {
- BindingResult result = (BindingResult) mav.getModel().get(BindingResult.MODEL_KEY_PREFIX + name);
- assertTrue("Expected bind errors for model attribute <" + name + ">", result.hasErrors());
- }
- }
- };
- }
-
- public static MvcResultMatcher viewName(final String viewName) {
- return new MvcResultMatcher() {
- public void match(MockHttpServletRequest request,
- MockHttpServletResponse response,
- Object handler,
- Exception handlerException,
- ModelAndView mav) {
- assertEquals("View name", viewName, mav.getViewName());
- }
- };
- }
-
- private abstract static class ModelAndViewResultMatcher implements MvcResultMatcher {
-
- public final void match(MockHttpServletRequest request,
- MockHttpServletResponse response,
- Object handler,
- Exception handlerException,
- ModelAndView mav) {
- assertTrue("No ModelAndView", mav != null);
- matchModelAndView(mav);
- }
-
- protected abstract void matchModelAndView(ModelAndView mav);
- }
-
-}
diff --git a/src/main/java/org/springframework/test/web/server/matcher/MvcResultMatchers.java b/src/main/java/org/springframework/test/web/server/matcher/MvcResultMatchers.java
deleted file mode 100644
index ed9636d..0000000
--- a/src/main/java/org/springframework/test/web/server/matcher/MvcResultMatchers.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright 2002-2011 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.server.matcher;
-
-import org.springframework.test.web.server.MvcResultMatcher;
-
-/**
- * The {@link MvcResultMatchers}s in this class overlap with other {@code *Matchers} in this package.
- * The intent is to compile a list {@link MvcResultMatcher}s recommended for common use.
- *
- */
-public class MvcResultMatchers {
-
- public static MvcResultMatcher status(int status) {
- return MockResponseMatchers.status(status);
- }
-
- public static MvcResultMatcher contentType(String contentType) {
- return MockResponseMatchers.contentType(contentType);
- }
-
- public static MvcResultMatcher responseBody(String content) {
- return MockResponseMatchers.responseBody(content);
- }
-
- public static MvcResultMatcher responseBodyContains(String text) {
- return MockResponseMatchers.responseBodyContains(text);
- }
-
- public static MvcResultMatcher forwardedUrl(String forwardUrl) {
- return MockResponseMatchers.forwardedUrl(forwardUrl);
- }
-
- public static MvcResultMatcher redirectedUrl(String redirectUrl) {
- return MockResponseMatchers.redirectedUrl(redirectUrl);
- }
-
- public static MvcResultMatcher viewName(String viewName) {
- return ModelAndViewMatchers.viewName(viewName);
- }
-
- public static MvcResultMatcher noBindingErrors() {
- return ModelAndViewMatchers.noBindingErrors();
- }
-
- public static MvcResultMatcher modelAttributesWithErrors(String...names) {
- return ModelAndViewMatchers.modelAttributesWithErrors(names);
- }
-
- public static MvcResultMatcher modelAttributesPresent(String...names) {
- return ModelAndViewMatchers.modelAttributesPresent(names);
- }
-
- public static MvcResultMatcher loggingMatcher() {
- return new LoggingMatcher();
- }
-
-}
diff --git a/src/main/java/org/springframework/test/web/server/MvcResultMatcher.java b/src/main/java/org/springframework/test/web/server/package-info.java
similarity index 54%
rename from src/main/java/org/springframework/test/web/server/MvcResultMatcher.java
rename to src/main/java/org/springframework/test/web/server/package-info.java
index b42bcbf..dcb42c0 100644
--- a/src/main/java/org/springframework/test/web/server/MvcResultMatcher.java
+++ b/src/main/java/org/springframework/test/web/server/package-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2011 the original author or authors.
+ * Copyright 2011-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.
@@ -14,22 +14,8 @@
* limitations under the License.
*/
-package org.springframework.test.web.server;
-
-import org.springframework.mock.web.MockHttpServletRequest;
-import org.springframework.mock.web.MockHttpServletResponse;
-import org.springframework.web.servlet.ModelAndView;
-
/**
- * Defines a match operation on the results of a processed request.
- *
+ * Contains server-side support for testing Spring MVC applications.
+ * @see org.springframework.test.web.server.MockMvc
*/
-public interface MvcResultMatcher {
-
- void match(MockHttpServletRequest request,
- MockHttpServletResponse response,
- Object handler,
- Exception handlerException,
- ModelAndView mav);
-
-}
+package org.springframework.test.web.server;
diff --git a/src/main/java/org/springframework/test/web/server/request/MockHttpServletRequestBuilder.java b/src/main/java/org/springframework/test/web/server/request/MockHttpServletRequestBuilder.java
new file mode 100644
index 0000000..9a005ae
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/server/request/MockHttpServletRequestBuilder.java
@@ -0,0 +1,686 @@
+/*
+ * Copyright 2011-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.server.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.server.MockMvc;
+import org.springframework.test.web.server.RequestBuilder;
+import org.springframework.test.web.server.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}.
+ *
+ * Application tests will typically access this builder through the static
+ * factory methods in {@link MockMvcBuilders}.
+ *
+ * @author Rossen Stoyanchev
+ * @author Arjen Poutsma
+ */
+public class MockHttpServletRequestBuilder implements RequestBuilder, Mergeable {
+
+ private final UriComponents uriComponents;
+
+ private final HttpMethod method;
+
+ private final MultiValueMap headers = new LinkedMultiValueMap();
+
+ private String contentType;
+
+ private byte[] content;
+
+ private final MultiValueMap parameters = new LinkedMultiValueMap();
+
+ private final List cookies = new ArrayList();
+
+ private Locale locale;
+
+ private String characterEncoding;
+
+ private Principal principal;
+
+ private Boolean secure;
+
+ private final Map attributes = new LinkedHashMap();
+
+ private MockHttpSession session;
+
+ private final Map sessionAttributes = new LinkedHashMap();
+
+ private final Map flashAttributes = new LinkedHashMap();
+
+ private String contextPath = "";
+
+ private String servletPath = "";
+
+ private String pathInfo = ValueConstants.DEFAULT_NONE;
+
+ private final List postProcessors =
+ new ArrayList();
+
+
+ /**
+ * Package private constructor. To get an instance, use static factory
+ * methods in {@link MockMvcRequestBuilders}.
+ *
+ * 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 request body.
+ *
+ * @param content the body content
+ */
+ public MockHttpServletRequestBuilder body(byte[] content) {
+ this.content = content;
+ 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 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 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.
+ *
+ * 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.
+ *
+ *
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 HttpServletRequest.getContextPath()
+ */
+ 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.
+ *
+ *
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 HttpServletRequest.getServletPath()
+ */
+ 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.
+ *
+ *
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 "/".
+ *
+ *
If specified, the pathInfo will be used as is.
+ *
+ * @see HttpServletRequest.getServletPath()
+ */
+ 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}.
+ */
+ 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
+ */
+ 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}.
+ */
+ 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> entry : this.uriComponents.getQueryParams().entrySet()) {
+ for (String value : entry.getValue()) {
+ request.addParameter(
+ UriUtils.decode(entry.getKey(), "UTF-8"),
+ UriUtils.decode(value, "UTF-8"));
+ }
+ }
+ }
+ 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");
+ }
+
+ return request;
+ }
+
+ /**
+ * Creates a new {@link MockHttpServletRequest} based on the given
+ * {@link ServletContext}. Can be overridden in sub-classes.
+ */
+ 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 void addToMultiValueMap(MultiValueMap 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 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);
+ }
+
+}
diff --git a/src/main/java/org/springframework/test/web/server/request/MockMultipartHttpServletRequestBuilder.java b/src/main/java/org/springframework/test/web/server/request/MockMultipartHttpServletRequestBuilder.java
new file mode 100644
index 0000000..6353a76
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/server/request/MockMultipartHttpServletRequestBuilder.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2011 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.server.request;
+
+import java.net.URI;
+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
+ */
+public class MockMultipartHttpServletRequestBuilder extends MockHttpServletRequestBuilder {
+
+ private final List files = new ArrayList();
+
+
+ /**
+ * Package private constructor. Use static factory methods in
+ * {@link MockMvcRequestBuilders}.
+ *
+ * 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 MockMultipartHttpServletRequestBuilder)) {
+ throw new IllegalArgumentException("Cannot merge with [" + parent.getClass().getName() + "]");
+ }
+
+ super.merge(parent);
+
+ MockMultipartHttpServletRequestBuilder parentBuilder = (MockMultipartHttpServletRequestBuilder) parent;
+ this.files.addAll(parentBuilder.files);
+
+ return this;
+ }
+
+ @Override
+ protected final MockHttpServletRequest createServletRequest(ServletContext servletContext) {
+ MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest();
+ for (MockMultipartFile file : this.files) {
+ request.addFile(file);
+ }
+ return request;
+ }
+
+}
diff --git a/src/main/java/org/springframework/test/web/server/request/MockMvcRequestBuilders.java b/src/main/java/org/springframework/test/web/server/request/MockMvcRequestBuilders.java
new file mode 100644
index 0000000..58719bb
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/server/request/MockMvcRequestBuilders.java
@@ -0,0 +1,97 @@
+/*
+ * 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.server.request;
+
+import org.springframework.http.HttpMethod;
+import org.springframework.test.web.server.RequestBuilder;
+
+/**
+ * Static factory methods for {@link RequestBuilder}s.
+ *
+ *
Eclipse users: 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 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... urlVars) {
+ return new MockHttpServletRequestBuilder(httpMethod, urlTemplate, urlVars);
+ }
+
+ /**
+ * 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);
+ }
+
+}
diff --git a/src/main/java/org/springframework/test/web/server/request/RequestPostProcessor.java b/src/main/java/org/springframework/test/web/server/request/RequestPostProcessor.java
new file mode 100644
index 0000000..d8bfd26
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/server/request/RequestPostProcessor.java
@@ -0,0 +1,44 @@
+/*
+ * 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.server.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}.
+ *
+ *
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
+ */
+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);
+
+}
diff --git a/src/main/java/org/springframework/test/web/server/request/package-info.java b/src/main/java/org/springframework/test/web/server/request/package-info.java
new file mode 100644
index 0000000..27377a4
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/server/request/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2011-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.server.RequestBuilder}
+ * implementations. Use
+ * {@link org.springframework.test.web.server.request.MockMvcRequestBuilders}
+ * to gain access to instances of those implementations.
+ */
+package org.springframework.test.web.server.request;
diff --git a/src/main/java/org/springframework/test/web/server/result/ContentResultMatchers.java b/src/main/java/org/springframework/test/web/server/result/ContentResultMatchers.java
new file mode 100644
index 0000000..7206608
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/server/result/ContentResultMatchers.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2011-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.server.result;
+
+import static org.springframework.test.web.AssertionErrors.assertEquals;
+import static org.springframework.test.web.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.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.server.MvcResult;
+import org.springframework.test.web.server.ResultMatcher;
+import org.springframework.test.web.support.XmlExpectationsHelper;
+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.
+ */
+ public ResultMatcher mimeType(String contentType) {
+ return mimeType(MediaType.parseMediaType(contentType));
+ }
+
+ /**
+ * Assert the ServletResponse content type after parsing it as a MediaType.
+ */
+ public ResultMatcher mimeType(final MediaType contentType) {
+ return new ResultMatcher() {
+ 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 character encoding in the ServletResponse.
+ * @see HttpServletResponse#getCharacterEncoding()
+ */
+ public ResultMatcher encoding(final String characterEncoding) {
+ return new ResultMatcher() {
+ 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}.
+ *
+ * mockMvc.perform(get("/path"))
+ * .andExpect(content(containsString("text")));
+ *
+ */
+ public ResultMatcher string(final Matcher super String> matcher) {
+ return new ResultMatcher() {
+ public void match(MvcResult result) throws Exception {
+ MatcherAssert.assertThat("Response content", result.getResponse().getContentAsString(), matcher);
+ }
+ };
+ }
+
+ /**
+ * Assert the response body content as a String.
+ */
+ public ResultMatcher string(String content) {
+ return string(Matchers.equalTo(content));
+ }
+
+ /**
+ * Assert the response body content as a byte array.
+ */
+ public ResultMatcher bytes(final byte[] expectedContent) {
+ return new ResultMatcher() {
+ public void match(MvcResult result) throws Exception {
+ byte[] content = result.getResponse().getContentAsByteArray();
+ MatcherAssert.assertThat("Response content", content, Matchers.equalTo(expectedContent));
+ }
+ };
+ }
+
+ /**
+ * 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.
+ *
+ * Use of this matcher requires the XMLUnit 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() {
+ public void match(MvcResult result) throws Exception {
+ String content = result.getResponse().getContentAsString();
+ xmlHelper.assertXmlEqual(xmlContent, content);
+ }
+ };
+ }
+
+ // TODO: XML validation
+
+ /**
+ * Parse the response content as {@link Node} and apply the given Hamcrest
+ * {@link Matcher}.
+ *
+ * @see org.hamcrest.Matchers#hasXPath
+ */
+ public ResultMatcher node(final Matcher super Node> matcher) {
+ return new ResultMatcher() {
+ 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 xml-matchers
+ */
+ public ResultMatcher source(final Matcher super Source> matcher) {
+ return new ResultMatcher() {
+ public void match(MvcResult result) throws Exception {
+ String content = result.getResponse().getContentAsString();
+ xmlHelper.assertSource(content, matcher);
+ }
+ };
+ }
+
+}
diff --git a/src/main/java/org/springframework/test/web/server/result/CookieResultMatchers.java b/src/main/java/org/springframework/test/web/server/result/CookieResultMatchers.java
new file mode 100644
index 0000000..4bb14d2
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/server/result/CookieResultMatchers.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright 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.server.result;
+
+import static org.springframework.test.web.AssertionErrors.assertEquals;
+import static org.springframework.test.web.AssertionErrors.assertTrue;
+
+import javax.servlet.http.Cookie;
+
+import org.hamcrest.Matcher;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.springframework.test.web.server.MvcResult;
+import org.springframework.test.web.server.ResultMatcher;
+
+/**
+ * Factory for response cookie assertions. An instance of this class is
+ * typically accessed via {@link MockMvcResultMatchers#cookie()}.
+ *
+ * @author Rossen Stoyanchev
+ * @author Thomas Bruyelle
+ */
+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() {
+ public void match(MvcResult result) {
+ Cookie cookie = result.getResponse().getCookie(name);
+ assertTrue("Response cookie not found: " + name, cookie != null);
+ MatcherAssert.assertThat("Response cookie", cookie.getValue(), matcher);
+ }
+ };
+ }
+
+ /**
+ * Assert a cookie value.
+ */
+ public ResultMatcher value(String name, String value) {
+ return value(name, Matchers.equalTo(value));
+ }
+
+ /**
+ * 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() {
+ 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() {
+ 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() {
+ public void match(MvcResult result) {
+ Cookie cookie = result.getResponse().getCookie(name);
+ assertTrue("No cookie with name: " + name, cookie != null);
+ MatcherAssert.assertThat("Response cookie maxAge", cookie.getMaxAge(), matcher);
+ }
+ };
+ }
+
+ /**
+ * Assert a cookie's maxAge value.
+ */
+ public ResultMatcher maxAge(String name, int maxAge) {
+ return maxAge(name, Matchers.equalTo(maxAge));
+ }
+
+ /**
+ * Assert a cookie path with a Hamcrest {@link Matcher}.
+ */
+ public ResultMatcher path(final String name, final Matcher super String> matcher) {
+ return new ResultMatcher() {
+ public void match(MvcResult result) throws Exception {
+ Cookie cookie = result.getResponse().getCookie(name);
+ MatcherAssert.assertThat("Response cookie path", cookie.getPath(), matcher);
+ }
+ };
+ }
+
+ public ResultMatcher path(String name, String path) {
+ return path(name, Matchers.equalTo(path));
+ }
+
+ /**
+ * Assert a cookie's domain with a Hamcrest {@link Matcher}.
+ */
+ public ResultMatcher domain(final String name, final Matcher super String> matcher) {
+ return new ResultMatcher() {
+ public void match(MvcResult result) throws Exception {
+ Cookie cookie = result.getResponse().getCookie(name);
+ MatcherAssert.assertThat("Response cookie domain", cookie.getDomain(), matcher);
+ }
+ };
+ }
+
+ /**
+ * Assert a cookie's domain value.
+ */
+ public ResultMatcher domain(String name, String domain) {
+ return domain(name, Matchers.equalTo(domain));
+ }
+
+ /**
+ * Assert a cookie's comment with a Hamcrest {@link Matcher}.
+ */
+ public ResultMatcher comment(final String name, final Matcher super String> matcher) {
+ return new ResultMatcher() {
+ public void match(MvcResult result) throws Exception {
+ Cookie cookie = result.getResponse().getCookie(name);
+ MatcherAssert.assertThat("Response cookie comment", cookie.getComment(), matcher);
+ }
+ };
+ }
+
+ /**
+ * Assert a cookie's comment value.
+ */
+ public ResultMatcher comment(String name, String comment) {
+ return comment(name, Matchers.equalTo(comment));
+ }
+
+ /**
+ * Assert a cookie's version with a Hamcrest {@link Matcher}
+ */
+ public ResultMatcher version(final String name, final Matcher super Integer> matcher) {
+ return new ResultMatcher() {
+ public void match(MvcResult result) throws Exception {
+ Cookie cookie = result.getResponse().getCookie(name);
+ MatcherAssert.assertThat("Response cookie version", cookie.getVersion(), matcher);
+ }
+ };
+ }
+
+ /**
+ * Assert a cookie's version value.
+ */
+ public ResultMatcher version(String name, int version) {
+ return version(name, Matchers.equalTo(version));
+ }
+
+ /**
+ * 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() {
+ public void match(MvcResult result) throws Exception {
+ Cookie cookie = result.getResponse().getCookie(name);
+ assertEquals("Response cookie secure", secure, cookie.getSecure());
+ }
+ };
+ }
+}
diff --git a/src/main/java/org/springframework/test/web/server/result/FlashAttributeResultMatchers.java b/src/main/java/org/springframework/test/web/server/result/FlashAttributeResultMatchers.java
new file mode 100644
index 0000000..ab9ced4
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/server/result/FlashAttributeResultMatchers.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2011-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.server.result;
+
+import static org.springframework.test.web.AssertionErrors.*;
+
+import org.hamcrest.Matcher;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.springframework.test.web.server.MvcResult;
+import org.springframework.test.web.server.ResultMatcher;
+
+/**
+ * Factory for "output" flash attribute assertions. An instance of this class is
+ * typically accessed via {@link MockMvcResultMatchers#flash()}.
+ *
+ * @author Rossen Stoyanchev
+ */
+public class FlashAttributeResultMatchers {
+
+
+ /**
+ * Protected constructor.
+ * Use {@link MockMvcResultMatchers#flash()}.
+ */
+ protected FlashAttributeResultMatchers() {
+ }
+
+ /**
+ * Assert a flash attribute's value with the given Hamcrest {@link Matcher}.
+ */
+ public ResultMatcher attribute(final String name, final Matcher matcher) {
+ return new ResultMatcher() {
+ @SuppressWarnings("unchecked")
+ public void match(MvcResult result) throws Exception {
+ MatcherAssert.assertThat("Flash attribute", (T) result.getFlashMap().get(name), matcher);
+ }
+ };
+ }
+
+ /**
+ * Assert a flash attribute's value.
+ */
+ public ResultMatcher attribute(final String name, final Object value) {
+ return attribute(name, Matchers.equalTo(value));
+ }
+
+ /**
+ * Assert the existence of the given flash attributes.
+ */
+ public ResultMatcher attributeExists(final String... names) {
+ return new ResultMatcher() {
+ public void match(MvcResult result) throws Exception {
+ for (String name : names) {
+ attribute(name, Matchers.notNullValue()).match(result);
+ }
+ }
+ };
+ }
+
+ /**
+ * Assert the number of flash attributes.
+ */
+ public ResultMatcher attributeCount(final int count) {
+ return new ResultMatcher() {
+ public void match(MvcResult result) throws Exception {
+ assertEquals("FlashMap size", count, result.getFlashMap().size());
+ }
+ };
+ }
+
+}
diff --git a/src/main/java/org/springframework/test/web/server/result/HandlerResultMatchers.java b/src/main/java/org/springframework/test/web/server/result/HandlerResultMatchers.java
new file mode 100644
index 0000000..b69e7f8
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/server/result/HandlerResultMatchers.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2011-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.server.result;
+
+import static org.springframework.test.web.AssertionErrors.assertEquals;
+import static org.springframework.test.web.AssertionErrors.assertTrue;
+
+import java.lang.reflect.Method;
+
+import org.hamcrest.Matcher;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.springframework.test.web.server.MvcResult;
+import org.springframework.test.web.server.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
+ */
+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() {
+ 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}.
+ *
+ * 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() {
+ public void match(MvcResult result) throws Exception {
+ Object handler = result.getHandler();
+ assertTrue("No handler: ", handler != null);
+ assertTrue("Not a HandlerMethod: " + handler, HandlerMethod.class.isInstance(handler));
+ MatcherAssert.assertThat("HandlerMethod", ((HandlerMethod) handler).getMethod().getName(), matcher);
+ }
+ };
+ }
+
+ /**
+ * Assert the name of the controller method that processed the request.
+ *
+ *
Use of this method implies annotated controllers are processed with
+ * {@link RequestMappingHandlerMapping} and {@link RequestMappingHandlerAdapter}.
+ */
+ public ResultMatcher methodName(final String name) {
+ return methodName(Matchers.equalTo(name));
+ }
+
+ /**
+ * Assert the controller method that processed the request.
+ *
+ *
Use of this method implies annotated controllers are processed with
+ * {@link RequestMappingHandlerMapping} and {@link RequestMappingHandlerAdapter}.
+ */
+ public ResultMatcher method(final Method method) {
+ return new ResultMatcher() {
+ public void match(MvcResult result) throws Exception {
+ Object handler = result.getHandler();
+ assertTrue("No handler: ", handler != null);
+ assertTrue("Not a HandlerMethod: " + handler, HandlerMethod.class.isInstance(handler));
+ assertEquals("HandlerMethod", method, ((HandlerMethod) handler).getMethod());
+ }
+ };
+ }
+
+}
diff --git a/src/main/java/org/springframework/test/web/server/result/HeaderResultMatchers.java b/src/main/java/org/springframework/test/web/server/result/HeaderResultMatchers.java
new file mode 100644
index 0000000..6c5c51c
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/server/result/HeaderResultMatchers.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2011-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.server.result;
+
+import static org.springframework.test.web.AssertionErrors.assertEquals;
+
+import org.hamcrest.Matcher;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.springframework.test.web.server.MvcResult;
+import org.springframework.test.web.server.ResultMatcher;
+
+/**
+ * Factory for response header assertions. An instance of this
+ * class is usually accessed via {@link MockMvcResultMatchers#header()}.
+ *
+ * @author Rossen Stoyanchev
+ */
+public class HeaderResultMatchers {
+
+
+ /**
+ * Protected constructor.
+ * Use {@link MockMvcResultMatchers#header()}.
+ */
+ protected HeaderResultMatchers() {
+ }
+
+ /**
+ * Assert a response header with the given Hamcrest {@link Matcher}.
+ */
+ public ResultMatcher string(final String name, final Matcher super String> matcher) {
+ return new ResultMatcher() {
+ public void match(MvcResult result) {
+ MatcherAssert.assertThat("Response header", result.getResponse().getHeader(name), matcher);
+ }
+ };
+ }
+
+ /**
+ * Assert the primary value of a response header as a {@link String}.
+ */
+ public ResultMatcher string(final String name, final String value) {
+ return string(name, Matchers.equalTo(value));
+ }
+
+ /**
+ * Assert the primary value of a response header as a {@link Long}.
+ */
+ public ResultMatcher longValue(final String name, final long value) {
+ return new ResultMatcher() {
+ public void match(MvcResult result) {
+ assertEquals("Response header " + name, value, Long.parseLong(result.getResponse().getHeader(name)));
+ }
+ };
+ }
+
+}
diff --git a/src/main/java/org/springframework/test/web/server/result/JsonPathResultMatchers.java b/src/main/java/org/springframework/test/web/server/result/JsonPathResultMatchers.java
new file mode 100644
index 0000000..6ffba3d
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/server/result/JsonPathResultMatchers.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2011-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.server.result;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.instanceOf;
+
+import java.util.List;
+
+import org.hamcrest.Matcher;
+import org.springframework.test.web.server.MvcResult;
+import org.springframework.test.web.server.ResultMatcher;
+import org.springframework.test.web.support.JsonPathExpectationsHelper;
+
+/**
+ * Factory for assertions on the response content using JSONPath expressions.
+ * An instance of this class is typically accessed via
+ * {@link MockMvcResultMatchers#jsonPpath}.
+ *
+ * @author Rossen Stoyanchev
+ */
+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 ResultMatcher value(final Matcher matcher) {
+ return new ResultMatcher() {
+ 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(Object value) {
+ return value(equalTo(value));
+ }
+
+ /**
+ * Evaluate the JSONPath and assert that content exists.
+ */
+ public ResultMatcher exists() {
+ return new ResultMatcher() {
+ 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() {
+ 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 value(instanceOf(List.class));
+ }
+}
diff --git a/src/main/java/org/springframework/test/web/server/result/MockMvcResultHandlers.java b/src/main/java/org/springframework/test/web/server/result/MockMvcResultHandlers.java
new file mode 100644
index 0000000..bdb0d19
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/server/result/MockMvcResultHandlers.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2011-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.server.result;
+
+import org.springframework.test.web.server.MvcResult;
+import org.springframework.test.web.server.ResultHandler;
+import org.springframework.util.CollectionUtils;
+
+/**
+ * Static, factory methods for {@link ResultHandler}-based result actions.
+ *
+ * Eclipse users: consider adding this class as a Java editor
+ * favorite. To navigate, open the Preferences and type "favorites".
+ *
+ * @author Rossen Stoyanchev
+ */
+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() {
+
+ public void printHeading(String heading) {
+ System.out.println();
+ System.out.println(String.format("%20s:", heading));
+ }
+
+ 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));
+ }
+ });
+ }
+ }
+}
diff --git a/src/main/java/org/springframework/test/web/server/result/MockMvcResultMatchers.java b/src/main/java/org/springframework/test/web/server/result/MockMvcResultMatchers.java
new file mode 100644
index 0000000..e964b67
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/server/result/MockMvcResultMatchers.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2011-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.server.result;
+
+import static org.springframework.test.web.AssertionErrors.assertEquals;
+
+import java.util.Map;
+
+import javax.xml.xpath.XPathExpressionException;
+
+import org.hamcrest.Matcher;
+import org.springframework.test.web.server.MvcResult;
+import org.springframework.test.web.server.ResultMatcher;
+
+/**
+ * Static, factory methods for {@link ResultMatcher}-based result actions.
+ *
+ *
Eclipse users: consider adding this class as a Java editor
+ * favorite. To navigate, open the Preferences and type "favorites".
+ *
+ * @author Rossen Stoyanchev
+ */
+public abstract class MockMvcResultMatchers {
+
+
+ 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.
+ */
+ public static ResultMatcher forwardedUrl(final String expectedUrl) {
+ return new ResultMatcher() {
+ public void match(MvcResult result) {
+ assertEquals("Forwarded URL", expectedUrl, result.getResponse().getForwardedUrl());
+ }
+ };
+ }
+
+ /**
+ * Asserts the request was redirected to the given URL.
+ */
+ public static ResultMatcher redirectedUrl(final String expectedUrl) {
+ return new ResultMatcher() {
+ public void match(MvcResult result) {
+ assertEquals("Redirected URL", 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 JSONPath 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 JSONPath 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 ResultMatcher jsonPath(String expression, Matcher 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 namespaces, Object... args)
+ throws XPathExpressionException {
+
+ return new XpathResultMatchers(expression, namespaces, args);
+ }
+
+ /**
+ * Access to response cookie assertions.
+ */
+ public static CookieResultMatchers cookie() {
+ return new CookieResultMatchers();
+ }
+
+}
diff --git a/src/main/java/org/springframework/test/web/server/result/ModelResultMatchers.java b/src/main/java/org/springframework/test/web/server/result/ModelResultMatchers.java
new file mode 100644
index 0000000..675d3c4
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/server/result/ModelResultMatchers.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2011-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.server.result;
+
+import static org.springframework.test.web.AssertionErrors.assertEquals;
+import static org.springframework.test.web.AssertionErrors.assertTrue;
+
+import org.hamcrest.Matcher;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.springframework.test.web.server.MvcResult;
+import org.springframework.test.web.server.ResultMatcher;
+import org.springframework.validation.BindingResult;
+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
+ */
+public class ModelResultMatchers {
+
+
+ /**
+ * Protected constructor.
+ * Use {@link MockMvcResultMatchers#model()}.
+ */
+ protected ModelResultMatchers() {
+ }
+
+ /**
+ * Assert a model attribute value with the given Hamcrest {@link Matcher}.
+ */
+ public ResultMatcher attribute(final String name, final Matcher matcher) {
+ return new ResultMatcher() {
+ @SuppressWarnings("unchecked")
+ public void match(MvcResult result) throws Exception {
+ ModelAndView mav = result.getModelAndView();
+ assertTrue("No ModelAndView found", mav != null);
+ MatcherAssert.assertThat("Model attribute '" + name + "'", (T) mav.getModel().get(name), matcher);
+ }
+ };
+ }
+
+ /**
+ * Assert a model attribute value.
+ */
+ public ResultMatcher attribute(String name, Object value) {
+ return attribute(name, Matchers.equalTo(value));
+ }
+
+ /**
+ * Assert the given model attributes exist.
+ */
+ public ResultMatcher attributeExists(final String... names) {
+ return new ResultMatcher() {
+ public void match(MvcResult result) throws Exception {
+ assertTrue("No ModelAndView found", result.getModelAndView() != null);
+ for (String name : names) {
+ attribute(name, Matchers.notNullValue()).match(result);
+ }
+ }
+ };
+ }
+
+ /**
+ * Assert the given model attribute(s) have errors.
+ */
+ public ResultMatcher attributeHasErrors(final String... names) {
+ return new ResultMatcher() {
+ 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() {
+ 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() {
+ 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 model has no errors.
+ */
+ public ResultMatcher hasNoErrors() {
+ return new ResultMatcher() {
+ public void match(MvcResult result) throws Exception {
+ ModelAndView mav = getModelAndView(result);
+ for (Object value : mav.getModel().values()) {
+ if (value instanceof BindingResult) {
+ assertTrue("Unexpected binding error(s): " + value, !((BindingResult) value).hasErrors());
+ }
+ }
+ }
+ };
+ }
+
+ /**
+ * Assert the number of model attributes.
+ */
+ public ResultMatcher size(final int size) {
+ return new ResultMatcher() {
+ 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;
+ }
+
+}
diff --git a/src/main/java/org/springframework/test/web/server/result/PrintingResultHandler.java b/src/main/java/org/springframework/test/web/server/result/PrintingResultHandler.java
new file mode 100644
index 0000000..d71d135
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/server/result/PrintingResultHandler.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright 2011-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.server.result;
+
+import java.util.Enumeration;
+
+import org.springframework.http.HttpHeaders;
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.mock.web.MockHttpServletResponse;
+import org.springframework.test.web.server.MvcResult;
+import org.springframework.test.web.server.ResultHandler;
+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
+ */
+public class PrintingResultHandler implements ResultHandler {
+
+ 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.
+ */
+ public final void handle(MvcResult result) throws Exception {
+
+ this.printer.printHeading("MockHttpServletRequest");
+ printRequest(result.getRequest());
+
+ this.printer.printHeading("Handler");
+ printHandler(result.getHandler(), result.getInterceptors());
+
+ 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", request.getParameterMap());
+ this.printer.printValue("Headers", getRequestHeaders(request));
+ }
+
+ protected static HttpHeaders getRequestHeaders(MockHttpServletRequest request) {
+ HttpHeaders headers = new HttpHeaders();
+ Enumeration> names = request.getHeaderNames();
+ while (names.hasMoreElements()) {
+ String name = (String) names.nextElement();
+ Enumeration values = request.getHeaders(name);
+ while (values.hasMoreElements()) {
+ headers.add(name, values.nextElement());
+ }
+ }
+ return headers;
+ }
+
+ /** 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 static 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);
+ }
+
+}
diff --git a/src/main/java/org/springframework/test/web/server/result/RequestResultMatchers.java b/src/main/java/org/springframework/test/web/server/result/RequestResultMatchers.java
new file mode 100644
index 0000000..29d35f1
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/server/result/RequestResultMatchers.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2011-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.server.result;
+
+import org.hamcrest.Matcher;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.springframework.test.web.server.MvcResult;
+import org.springframework.test.web.server.ResultMatcher;
+
+/**
+ * Factory for assertions on the request. An instance of this class is
+ * typically accessed via {@link MockMvcResultMatchers#request()}.
+ *
+ * @author Rossen Stoyanchev
+ */
+public class RequestResultMatchers {
+
+
+ /**
+ * Protected constructor.
+ * Use {@link MockMvcResultMatchers#request()}.
+ */
+ protected RequestResultMatchers() {
+ }
+
+ /**
+ * Assert a request attribute value with the given Hamcrest {@link Matcher}.
+ */
+ public ResultMatcher attribute(final String name, final Matcher matcher) {
+ return new ResultMatcher() {
+ @SuppressWarnings("unchecked")
+ public void match(MvcResult result) {
+ T value = (T) result.getRequest().getAttribute(name);
+ MatcherAssert.assertThat("Request attribute: ", value, matcher);
+ }
+ };
+ }
+
+ /**
+ * Assert a request attribute value.
+ */
+ public ResultMatcher attribute(String name, Object value) {
+ return attribute(name, Matchers.equalTo(value));
+ }
+
+ /**
+ * Assert a session attribute value with the given Hamcrest {@link Matcher}.
+ */
+ public ResultMatcher sessionAttribute(final String name, final Matcher matcher) {
+ return new ResultMatcher() {
+ @SuppressWarnings("unchecked")
+ public void match(MvcResult result) {
+ T value = (T) result.getRequest().getSession().getAttribute(name);
+ MatcherAssert.assertThat("Request attribute: ", value, matcher);
+ }
+ };
+ }
+
+ /**
+ * Assert a session attribute value..
+ */
+ public ResultMatcher sessionAttribute(String name, Object value) {
+ return sessionAttribute(name, Matchers.equalTo(value));
+ }
+
+}
diff --git a/src/main/java/org/springframework/test/web/server/result/StatusResultMatchers.java b/src/main/java/org/springframework/test/web/server/result/StatusResultMatchers.java
new file mode 100644
index 0000000..c0f6241
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/server/result/StatusResultMatchers.java
@@ -0,0 +1,540 @@
+/*
+ * Copyright 2011-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.server.result;
+
+import static org.springframework.test.web.AssertionErrors.assertEquals;
+
+import org.hamcrest.Matcher;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.springframework.http.HttpStatus;
+import org.springframework.test.web.server.MvcResult;
+import org.springframework.test.web.server.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
+ */
+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 matcher) {
+ return new ResultMatcher() {
+ public void match(MvcResult result) throws Exception {
+ MatcherAssert.assertThat("Status: ", result.getResponse().getStatus(), matcher);
+ }
+ };
+ }
+
+ /**
+ * Assert the response status code is equal to an integer value.
+ */
+ public ResultMatcher is(int status) {
+ return is(Matchers.equalTo(status));
+ }
+
+
+ /**
+ * Assert the Servlet response error message with the given Hamcrest {@link Matcher}.
+ */
+ public ResultMatcher reason(final Matcher super String> matcher) {
+ return new ResultMatcher() {
+ public void match(MvcResult result) throws Exception {
+ MatcherAssert.assertThat("Status reason: ", result.getResponse().getErrorMessage(), matcher);
+ }
+ };
+ }
+
+ /**
+ * Assert the Servlet response error message.
+ */
+ public ResultMatcher reason(String reason) {
+ return reason(Matchers.equalTo(reason));
+ }
+
+ /**
+ * 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).
+ */
+ public ResultMatcher isInsufficientSpaceOnResource() {
+ return matcher(HttpStatus.INSUFFICIENT_SPACE_ON_RESOURCE);
+ }
+
+ /**
+ * Assert the response status code is {@code HttpStatus.METHOD_FAILURE} (420).
+ */
+ public ResultMatcher isMethodFailure() {
+ return matcher(HttpStatus.METHOD_FAILURE);
+ }
+
+ /**
+ * Assert the response status code is {@code HttpStatus.DESTINATION_LOCKED} (421).
+ */
+ 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() {
+ public void match(MvcResult result) {
+ assertEquals("Status", status.value(), result.getResponse().getStatus());
+ }
+ };
+ }
+
+}
diff --git a/src/main/java/org/springframework/test/web/server/result/ViewResultMatchers.java b/src/main/java/org/springframework/test/web/server/result/ViewResultMatchers.java
new file mode 100644
index 0000000..39a081e
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/server/result/ViewResultMatchers.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2011-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.server.result;
+
+import static org.springframework.test.web.AssertionErrors.assertTrue;
+
+import org.hamcrest.Matcher;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.springframework.test.web.server.MvcResult;
+import org.springframework.test.web.server.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()}.
+ */
+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() {
+ public void match(MvcResult result) throws Exception {
+ ModelAndView mav = result.getModelAndView();
+ assertTrue("No ModelAndView found", mav != null);
+ MatcherAssert.assertThat("View name", mav.getViewName(), matcher);
+ }
+ };
+ }
+
+ /**
+ * Assert the selected view name.
+ */
+ public ResultMatcher name(final String name) {
+ return name(Matchers.equalTo(name));
+ }
+
+}
diff --git a/src/main/java/org/springframework/test/web/server/result/XpathResultMatchers.java b/src/main/java/org/springframework/test/web/server/result/XpathResultMatchers.java
new file mode 100644
index 0000000..f2d2fee
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/server/result/XpathResultMatchers.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2011-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.server.result;
+
+import java.util.Map;
+
+import javax.xml.xpath.XPathExpressionException;
+
+import org.hamcrest.Matcher;
+import org.hamcrest.Matchers;
+import org.springframework.test.web.server.MvcResult;
+import org.springframework.test.web.server.ResultMatcher;
+import org.springframework.test.web.support.XpathExpectationsHelper;
+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
+ */
+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 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() {
+ 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 node(Matchers.notNullValue());
+ }
+
+ /**
+ * Evaluate the XPath and assert that content doesn't exist.
+ */
+ public ResultMatcher doesNotExist() {
+ return node(Matchers.nullValue());
+ }
+
+ /**
+ * Evaluate the XPath and assert the number of nodes found with the given
+ * Hamcrest {@link Matcher}.
+ */
+ public ResultMatcher nodeCount(final Matcher matcher) {
+ return new ResultMatcher() {
+ 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(int count) {
+ return nodeCount(Matchers.equalTo(count));
+ }
+
+ /**
+ * 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() {
+ 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(String value) {
+ return string(Matchers.equalTo(value));
+ }
+
+ /**
+ * 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() {
+ 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(Double value) {
+ return number(Matchers.equalTo(value));
+ }
+
+ /**
+ * Evaluate the XPath and assert the {@link Boolean} value found.
+ */
+ public ResultMatcher booleanValue(final Boolean value) {
+ return new ResultMatcher() {
+ public void match(MvcResult result) throws Exception {
+ String content = result.getResponse().getContentAsString();
+ xpathHelper.assertBoolean(content, value);
+ }
+ };
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/springframework/test/web/server/result/package-info.java b/src/main/java/org/springframework/test/web/server/result/package-info.java
new file mode 100644
index 0000000..3ccba20
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/server/result/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2011-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.server.result.MockMvcResultMatchers}
+ * and {@link org.springframework.test.web.server.result.MockMvcResultHandlers}
+ * to access to instances of those implementations.
+ */
+package org.springframework.test.web.server.result;
diff --git a/src/main/java/org/springframework/test/web/server/setup/AbstractMockMvcBuilder.java b/src/main/java/org/springframework/test/web/server/setup/AbstractMockMvcBuilder.java
new file mode 100644
index 0000000..e90db9d
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/server/setup/AbstractMockMvcBuilder.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2011-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.server.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.server.MockMvcBuilder;
+import org.springframework.test.web.server.MockMvcBuilderSupport;
+import org.springframework.test.web.server.MockMvc;
+import org.springframework.test.web.server.RequestBuilder;
+import org.springframework.test.web.server.ResultHandler;
+import org.springframework.test.web.server.ResultMatcher;
+import org.springframework.util.Assert;
+import org.springframework.web.context.WebApplicationContext;
+
+/**
+ * Abstract implementation of {@link MockMvcBuilder} that implements the actual
+ * {@code build} method, provides convenient methods for configuring filters,
+ * default request properties, and global expectations, and delegates to an
+ * abstract method to obtain a {@link WebApplicationContext}.
+ *
+ * @author Rossen Stoyanchev
+ * @author Rob Winch
+ */
+public abstract class AbstractMockMvcBuilder>
+ extends MockMvcBuilderSupport implements MockMvcBuilder {
+
+ private List filters = new ArrayList();
+
+ private RequestBuilder defaultRequestBuilder;
+
+ private final List globalResultMatchers = new ArrayList();
+
+ private final List globalResultHandlers = new ArrayList();
+
+
+ /**
+ * Add filters mapped to any request (i.e. "/*"). For example:
+ *
+ *
+ * mockMvcBuilder.addFilters(springSecurityFilterChain);
+ *
+ *
+ * is the equivalent of the following web.xml configuration:
+ *
+ *
+ * <filter-mapping>
+ * <filter-name>springSecurityFilterChain</filter-name>
+ * <url-pattern>/*</url-pattern>
+ * </filter-mapping>
+ *
+ *
+ * Filters will be invoked in the order in which they are provided.
+ *
+ * @param filters the filters to add
+ */
+ @SuppressWarnings("unchecked")
+ public final 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:
+ *
+ *
+ * mockMvcBuilder.addFilters(myResourceFilter, "/resources/*");
+ *
+ *
+ * is the equivalent of:
+ *
+ *
+ * <filter-mapping>
+ * <filter-name>myResourceFilter</filter-name>
+ * <url-pattern>/resources/*</url-pattern>
+ * </filter-mapping>
+ *
+ *
+ * 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
+ * @return
+ */
+ @SuppressWarnings("unchecked")
+ public final 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.
+ *
+ * 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.server.request.MockMvcRequestBuilders}
+ * .
+ */
+ @SuppressWarnings("unchecked")
+ public final T defaultRequest(RequestBuilder requestBuilder) {
+ this.defaultRequestBuilder = requestBuilder;
+ return (T) this;
+ }
+
+ /**
+ * Define a global expectation that should always 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.server.result.MockMvcResultMatchers}
+ */
+ @SuppressWarnings("unchecked")
+ public final T alwaysExpect(ResultMatcher resultMatcher) {
+ this.globalResultMatchers.add(resultMatcher);
+ return (T) this;
+ }
+
+ /**
+ * Define a global action that should always 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.server.result.MockMvcResultHandlers}
+ */
+ @SuppressWarnings("unchecked")
+ public final T alwaysDo(ResultHandler resultHandler) {
+ this.globalResultHandlers.add(resultHandler);
+ return (T) this;
+ }
+
+ /**
+ * Build a {@link MockMvc} instance.
+ */
+ public final MockMvc build() {
+
+ WebApplicationContext webAppContext = initWebApplicationContext();
+ Assert.state(webAppContext != null, "WebApplicationContext not provided by concrete MockMvcBuilder");
+
+ ServletContext servletContext = webAppContext.getServletContext();
+ Assert.state(servletContext != null,"ServletContext not configured by concrete MockMvcBuilder");
+
+ Filter[] filterArray = this.filters.toArray(new Filter[this.filters.size()]);
+ MockServletConfig mockServletConfig = new MockServletConfig(servletContext);
+
+ return super.createMockMvc(filterArray, mockServletConfig, webAppContext,
+ this.defaultRequestBuilder, this.globalResultMatchers, this.globalResultHandlers);
+ }
+
+ /**
+ * Return the WebApplicationContext to use. The return value must not be
+ * {@code null}. Further, the {@code WebApplicationContext} should be
+ * configured with a {@code ServletContext}.
+ */
+ protected abstract WebApplicationContext initWebApplicationContext();
+
+}
diff --git a/src/main/java/org/springframework/test/web/server/setup/ConfigurableContextMockMvcBuilder.java b/src/main/java/org/springframework/test/web/server/setup/ConfigurableContextMockMvcBuilder.java
deleted file mode 100644
index 8643bc0..0000000
--- a/src/main/java/org/springframework/test/web/server/setup/ConfigurableContextMockMvcBuilder.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright 2002-2011 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.server.setup;
-
-import javax.servlet.RequestDispatcher;
-
-import org.springframework.context.ApplicationContextInitializer;
-import org.springframework.core.io.DefaultResourceLoader;
-import org.springframework.core.io.FileSystemResourceLoader;
-import org.springframework.core.io.ResourceLoader;
-import org.springframework.mock.web.MockRequestDispatcher;
-import org.springframework.mock.web.MockServletContext;
-import org.springframework.web.context.ConfigurableWebApplicationContext;
-import org.springframework.web.context.WebApplicationContext;
-
-/**
- * A {@link ContextMockMvcBuilder} variant that expects a Spring {@link ConfigurableWebApplicationContext} and
- * provides methods to further initialize the context by setting active profiles, applying
- * {@link ApplicationContextInitializer}s to it and so on.
- *
- */
-public class ConfigurableContextMockMvcBuilder extends ContextMockMvcBuilder {
-
- private final ConfigurableWebApplicationContext applicationContext;
-
- private String webResourceBasePath = "";
-
- private ResourceLoader webResourceLoader = new FileSystemResourceLoader();
-
- protected ConfigurableContextMockMvcBuilder(ConfigurableWebApplicationContext applicationContext) {
- super(applicationContext);
- this.applicationContext = applicationContext;
- }
-
- /**
- * Specify the location of web application root directory.
- *
- * If {@code isClasspathRelative} is {@code false} the directory path may be relative to the JVM working
- * directory (e.g. "src/main/webapp") or fully qualified (e.g. "file:///home/user/webapp"). Or otherwise it
- * should be relative to the classpath (e.g. "org/examples/myapp/config").
- *
- * @param warRootDir the Web application root directory (should not end with a slash)
- */
- public ConfigurableContextMockMvcBuilder configureWarRootDir(String warRootDir, boolean isClasspathRelative) {
- this.webResourceBasePath = warRootDir;
- this.webResourceLoader = isClasspathRelative ? new DefaultResourceLoader() : new FileSystemResourceLoader();
- return this;
- }
-
- public ConfigurableContextMockMvcBuilder activateProfiles(String...profiles) {
- applicationContext.getEnvironment().setActiveProfiles(profiles);
- return this;
- }
-
- @SuppressWarnings("unchecked")
- public
- ConfigurableContextMockMvcBuilder applyInitializers(ApplicationContextInitializer... initializers) {
-
- for (ApplicationContextInitializer initializer : initializers) {
- initializer.initialize((T) applicationContext);
- }
- return this;
- }
-
- @Override
- protected WebApplicationContext initApplicationContext() {
-
- MockServletContext servletContext = new MockServletContext(webResourceBasePath, webResourceLoader) {
- // For DefaultServletHttpRequestHandler ..
- public RequestDispatcher getNamedDispatcher(String path) {
- return (path.equals("default")) ?
- new MockRequestDispatcher(path) : super.getNamedDispatcher(path);
- }
- };
-
- applicationContext.setServletContext(servletContext);
- applicationContext.refresh();
-
- return applicationContext;
- }
-
-}
diff --git a/src/main/java/org/springframework/test/web/server/setup/ContextMockMvcBuilder.java b/src/main/java/org/springframework/test/web/server/setup/ContextMockMvcBuilder.java
index c0a2a1a..c3453b1 100644
--- a/src/main/java/org/springframework/test/web/server/setup/ContextMockMvcBuilder.java
+++ b/src/main/java/org/springframework/test/web/server/setup/ContextMockMvcBuilder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2011 the original author or authors.
+ * Copyright 2011-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.
@@ -16,127 +16,113 @@
package org.springframework.test.web.server.setup;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-import org.springframework.beans.BeanUtils;
-import org.springframework.beans.factory.BeanFactoryUtils;
-import org.springframework.beans.factory.NoSuchBeanDefinitionException;
-import org.springframework.core.OrderComparator;
-import org.springframework.test.web.server.AbstractMockMvcBuilder;
-import org.springframework.test.web.server.MockMvc;
-import org.springframework.util.Assert;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletContext;
+
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextInitializer;
+import org.springframework.core.io.DefaultResourceLoader;
+import org.springframework.core.io.FileSystemResourceLoader;
+import org.springframework.core.io.ResourceLoader;
+import org.springframework.mock.web.MockRequestDispatcher;
+import org.springframework.mock.web.MockServletContext;
+import org.springframework.web.context.ConfigurableWebApplicationContext;
import org.springframework.web.context.WebApplicationContext;
-import org.springframework.web.servlet.DispatcherServlet;
-import org.springframework.web.servlet.HandlerAdapter;
-import org.springframework.web.servlet.HandlerExceptionResolver;
-import org.springframework.web.servlet.HandlerMapping;
-import org.springframework.web.servlet.LocaleResolver;
-import org.springframework.web.servlet.RequestToViewNameTranslator;
-import org.springframework.web.servlet.ViewResolver;
-import org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping;
-import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver;
-import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter;
-import org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter;
-import org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter;
-import org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver;
-import org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping;
-import org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver;
-import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;
-import org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator;
-import org.springframework.web.servlet.view.InternalResourceViewResolver;
/**
- * Builds a {@link MockMvc} by detecting Spring MVC infrastructure components in a Spring
- * {@link WebApplicationContext}.
- *
+ * A MockMvcBuilder that discovers controllers and Spring MVC infrastructure
+ * components in a WebApplicationContext.
+ *
+ * Unlike {@link InitializedContextMockMvcBuilder}, which expects a fully
+ * initialized WebApplicationContext, this MockMvcBuilder provides methods to
+ * initialize various aspects of the WebApplicationContext such activating
+ * profiles, configuring the root of the webapp directory (classpath or file
+ * system-relative), and others.
+ *
+ * @author Rossen Stoyanchev
*/
-public class ContextMockMvcBuilder extends AbstractMockMvcBuilder {
+public class ContextMockMvcBuilder extends AbstractMockMvcBuilder {
- private final WebApplicationContext applicationContext;
+ private final ConfigurableWebApplicationContext webAppContext;
- public ContextMockMvcBuilder(WebApplicationContext applicationContext) {
- Assert.notNull(applicationContext, "ApplicationContext is required");
- this.applicationContext = applicationContext;
- }
+ private String webResourceBasePath = "";
- @Override
- protected WebApplicationContext initApplicationContext() {
- return applicationContext;
- }
-
- @Override
- protected List initHandlerMappings() {
- List result = getOrderedBeans(HandlerMapping.class);
- if (result.isEmpty()) {
- result.add(new BeanNameUrlHandlerMapping());
- result.add(new DefaultAnnotationHandlerMapping());
- }
- return result;
- }
-
- @Override
- protected List initHandlerAdapters() {
- List result = getOrderedBeans(HandlerAdapter.class);
- if (result.isEmpty()) {
- result.add(new HttpRequestHandlerAdapter());
- result.add(new SimpleControllerHandlerAdapter());
- result.add(new AnnotationMethodHandlerAdapter());
- }
- return result;
+ private ResourceLoader webResourceLoader = new FileSystemResourceLoader();
+
+
+ /**
+ * Protected constructor. Not intended for direct instantiation.
+ * @see MockMvcBuilders#annotationConfigSetup(Class...)
+ * @see MockMvcBuilders#xmlConfigSetup(String...)
+ */
+ public ContextMockMvcBuilder(ConfigurableWebApplicationContext applicationContext) {
+ this.webAppContext = applicationContext;
}
-
- @Override
- protected List initHandlerExceptionResolvers() {
- List result = getOrderedBeans(HandlerExceptionResolver.class);
- if (result.isEmpty()) {
- result.add(new AnnotationMethodHandlerExceptionResolver());
- result.add(new ResponseStatusExceptionResolver());
- result.add(new DefaultHandlerExceptionResolver());
- }
- return result;
+
+ /**
+ * Specify the location of the web application root directory.
+ * If {@code isClasspathRelative} is "false" the directory is interpreted either as being
+ * relative to the JVM working directory (e.g. "src/main/webapp") or as a fully qualified
+ * file system path (e.g. "file:///home/user/webapp").
+ *
Otherwise if {@code isClasspathRelative} is "true" the directory should be relative
+ * to the classpath (e.g. "org/examples/myapp/config").
+ *
+ * @param warRootDir the Web application root directory (should not end with a slash)
+ */
+ public ContextMockMvcBuilder configureWebAppRootDir(String warRootDir, boolean isClasspathRelative) {
+ this.webResourceBasePath = warRootDir;
+ this.webResourceLoader = isClasspathRelative ? new DefaultResourceLoader() : new FileSystemResourceLoader();
+ return this;
}
- @Override
- protected List initViewResolvers() {
- List result = getOrderedBeans(ViewResolver.class);
- if (result.isEmpty()) {
- result.add(new InternalResourceViewResolver());
- }
- return result;
+ /**
+ * Activate the given profiles before the application context is "refreshed".
+ */
+ public ContextMockMvcBuilder activateProfiles(String...profiles) {
+ this.webAppContext.getEnvironment().setActiveProfiles(profiles);
+ return this;
}
- private List getOrderedBeans(Class beanType) {
- List components = new ArrayList();
- Map beans =
- BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, beanType, true, false);
- if (!beans.isEmpty()) {
- components.addAll(beans.values());
- OrderComparator.sort(components);
+ /**
+ * Apply the given {@link ApplicationContextInitializer}s before the application context is "refreshed".
+ */
+ @SuppressWarnings("unchecked")
+ public
+ ContextMockMvcBuilder applyInitializers(ApplicationContextInitializer... initializers) {
+
+ for (ApplicationContextInitializer initializer : initializers) {
+ initializer.initialize((T) this.webAppContext);
}
- return components;
+ return this;
}
- @Override
- protected RequestToViewNameTranslator initViewNameTranslator() {
- String name = DispatcherServlet.REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME;
- return getBeanByName(name, RequestToViewNameTranslator.class, DefaultRequestToViewNameTranslator.class);
- }
@Override
- protected LocaleResolver initLocaleResolver() {
- String name = DispatcherServlet.LOCALE_RESOLVER_BEAN_NAME;
- return getBeanByName(name, LocaleResolver.class, AcceptHeaderLocaleResolver.class);
+ protected WebApplicationContext initWebApplicationContext() {
+
+ ServletContext servletContext = new MockServletContext(this.webResourceBasePath, this.webResourceLoader) {
+ // Required for DefaultServletHttpRequestHandler...
+ public RequestDispatcher getNamedDispatcher(String path) {
+ return (path.equals("default")) ? new MockRequestDispatcher(path) : super.getNamedDispatcher(path);
+ }
+ };
+
+ this.webAppContext.setServletContext(servletContext);
+ this.webAppContext.refresh();
+
+ return this.webAppContext;
}
- private T getBeanByName(String name, Class requiredType, Class extends T> defaultType) {
- try {
- return applicationContext.getBean(name, requiredType);
- }
- catch (NoSuchBeanDefinitionException ex) {
- return (defaultType != null) ? BeanUtils.instantiate(defaultType) : null;
- }
+ /**
+ * Set a parent context before the application context is "refreshed".
+ *
+ * The parent context is expected to have be fully initialized.
+ *
+ *
Caution: this method is potentially subject to change depending
+ * on the outcome of SPR-5243 and SPR-5613.
+ */
+ public ContextMockMvcBuilder setParentContext(ApplicationContext parentContext) {
+ this.webAppContext.setParent(parentContext);
+ return this;
}
-
}
diff --git a/src/main/java/org/springframework/test/web/server/setup/InitializedContextMockMvcBuilder.java b/src/main/java/org/springframework/test/web/server/setup/InitializedContextMockMvcBuilder.java
new file mode 100644
index 0000000..7c28e46
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/server/setup/InitializedContextMockMvcBuilder.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2011-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.server.setup;
+
+import org.springframework.util.Assert;
+import org.springframework.web.context.WebApplicationContext;
+
+/**
+ * A MockMvcBuilder that discovers controllers and Spring MVC infrastructure
+ * components in a WebApplicationContext.
+ *
+ * TODO: merge this into AbstractMockMvcBuilder in 3.2 becoming DefaultMockMvcBuilder
+ *
+ * @author Rossen Stoyanchev
+ */
+public class InitializedContextMockMvcBuilder extends AbstractMockMvcBuilder {
+
+ private final WebApplicationContext webAppContext;
+
+ /**
+ * Protected constructor. Not intended for direct instantiation.
+ * @see MockMvcBuilders#webApplicationContextSetup(WebApplicationContext)
+ */
+ protected InitializedContextMockMvcBuilder(WebApplicationContext wac) {
+ Assert.notNull(wac, "WebApplicationContext is required");
+ Assert.notNull(wac.getServletContext(), "WebApplicationContext must have a ServletContext");
+ this.webAppContext = wac;
+ }
+
+ @Override
+ protected WebApplicationContext initWebApplicationContext() {
+ return this.webAppContext;
+ }
+
+}
diff --git a/src/main/java/org/springframework/test/web/server/setup/MockMvcBuilders.java b/src/main/java/org/springframework/test/web/server/setup/MockMvcBuilders.java
index 9f5431d..fcbb66e 100644
--- a/src/main/java/org/springframework/test/web/server/setup/MockMvcBuilders.java
+++ b/src/main/java/org/springframework/test/web/server/setup/MockMvcBuilders.java
@@ -1,60 +1,86 @@
+/*
+ * Copyright 2011 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.server.setup;
+import javax.servlet.ServletContext;
+
import org.springframework.context.annotation.Configuration;
+import org.springframework.stereotype.Controller;
import org.springframework.test.web.server.MockMvc;
+import org.springframework.test.web.server.MockMvcBuilder;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.context.support.XmlWebApplicationContext;
+/**
+ * The main class to import to access all available {@link MockMvcBuilder}s.
+ *
+ * Eclipse users: consider adding this class as a Java editor
+ * favorite. To navigate, open the Preferences and type "favorites".
+ *
+ * @author Rossen Stoyanchev
+ */
public class MockMvcBuilders {
/**
- * Build a {@link MockMvc} from a set of controllers with @{@link RequestMapping} methods.
- *
- * @param controllers controllers with @{@link RequestMapping} methods to include in the setup
- */
- public static StandaloneMockMvcBuilder standaloneMvcSetup(Object...controllers) {
- return new StandaloneMockMvcBuilder(controllers);
- }
-
- /**
- * Create a {@link ConfigurableContextMockMvcBuilder} from Spring Java-based configuration.
- *
- * @param configurationClasses @{@link Configuration} classes to use to create a WebApplicationContext
+ * Build a {@link MockMvc} from Java-based Spring configuration.
+ * @param configClasses one or more @{@link Configuration} classes
*/
- public static ConfigurableContextMockMvcBuilder annotationConfigMvcSetup(Class>...configurationClasses) {
- Assert.notEmpty(configurationClasses, "At least one @Configuration class is required");
+ public static ContextMockMvcBuilder annotationConfigSetup(Class>... configClasses) {
+ Assert.notEmpty(configClasses, "At least one @Configuration class is required");
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
- context.register(configurationClasses);
- return new ConfigurableContextMockMvcBuilder(context);
+ context.register(configClasses);
+ return new ContextMockMvcBuilder(context);
}
-
+
/**
- * Create a {@link ConfigurableContextMockMvcBuilder} from Spring XML configuration.
- *
- * @param configLocations XML configuration file locations
For example:
- *
- * - {@code classpath:org/example/config/*-context.xml}
- *
- {@code file:src/main/webapp/WEB-INF/config/*-context.xml}
+ * Build a {@link MockMvc} from XML-based Spring configuration.
+ * @param configLocations XML configuration file locations:
+ *
+ * - {@code classpath:org/example/config/*-context.xml}
+ *
- {@code file:src/main/webapp/WEB-INF/config/*-context.xml}
+ *
- etc.
*
*/
- public static ConfigurableContextMockMvcBuilder xmlConfigMvcSetup(String...configLocations) {
+ public static ContextMockMvcBuilder xmlConfigSetup(String... configLocations) {
Assert.notEmpty(configLocations, "At least one XML config location is required");
XmlWebApplicationContext context = new XmlWebApplicationContext();
context.setConfigLocations(configLocations);
- return new ConfigurableContextMockMvcBuilder(context);
+ return new ContextMockMvcBuilder(context);
+ }
+
+ /**
+ * Build a {@link MockMvc} from a fully initialized {@link WebApplicationContext}
+ * The context must have been setup with a {@link ServletContext} and refreshed.
+ */
+ public static InitializedContextMockMvcBuilder webApplicationContextSetup(WebApplicationContext context) {
+ return new InitializedContextMockMvcBuilder(context);
}
/**
- * Bulid a {@link MockMvc} from a fully initialized {@link WebApplicationContext}.
- * This may be useful if you already have a context initialized through the Spring TestContext framework.
+ * Build a {@link MockMvc} by providing @{@link Controller} instances and configuring
+ * directly the required Spring MVC components rather than having them looked up in
+ * a Spring ApplicationContext.
+ * @param controllers one or more controllers with @{@link RequestMapping} methods
*/
- public static ContextMockMvcBuilder applicationContextMvcSetup(WebApplicationContext applicationContext) {
- Assert.notNull(applicationContext, "WebApplicationContext is required");
- Assert.notNull(applicationContext.getServletContext(), "WebApplicationContext must have a ServletContext");
- return new ContextMockMvcBuilder(applicationContext);
+ public static StandaloneMockMvcBuilder standaloneSetup(Object... controllers) {
+ return new StandaloneMockMvcBuilder(controllers);
}
}
diff --git a/src/main/java/org/springframework/test/web/server/setup/MockWebApplicationContext.java b/src/main/java/org/springframework/test/web/server/setup/MockWebApplicationContext.java
new file mode 100644
index 0000000..b5babda
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/server/setup/MockWebApplicationContext.java
@@ -0,0 +1,341 @@
+/*
+ * Copyright 2011-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.server.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.
+ *
+ * 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
+ */
+class MockWebApplicationContext 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 MockWebApplicationContext(ServletContext servletContext) {
+ this.servletContext = servletContext;
+ this.resourcePatternResolver = new ServletContextResourcePatternResolver(servletContext);
+ }
+
+ /**
+ * Returns an instance that can initialize {@link ApplicationContextAware} beans.
+ */
+ public AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException {
+ return this.beanFactory;
+ }
+
+ public ServletContext getServletContext() {
+ return this.servletContext;
+ }
+
+ //---------------------------------------------------------------------
+ // Implementation of ApplicationContext interface
+ //---------------------------------------------------------------------
+
+ public String getId() {
+ return this.id;
+ }
+
+ public String getApplicationName() {
+ return "";
+ }
+
+ public String getDisplayName() {
+ return this.displayName;
+ }
+
+ public long getStartupDate() {
+ return this.startupDate;
+ }
+
+ public ApplicationContext getParent() {
+ return null;
+ }
+
+ 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
+ //---------------------------------------------------------------------
+
+ public Object getBean(String name) throws BeansException {
+ return this.beanFactory.getBean(name);
+ }
+
+ public T getBean(String name, Class requiredType) throws BeansException {
+ return this.beanFactory.getBean(name, requiredType);
+ }
+
+ public T getBean(Class requiredType) throws BeansException {
+ return this.beanFactory.getBean(requiredType);
+ }
+
+ public Object getBean(String name, Object... args) throws BeansException {
+ return this.beanFactory.getBean(name, args);
+ }
+
+ public boolean containsBean(String name) {
+ return this.beanFactory.containsBean(name);
+ }
+
+ public boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
+ return this.beanFactory.isSingleton(name);
+ }
+
+ public boolean isPrototype(String name) throws NoSuchBeanDefinitionException {
+ return this.beanFactory.isPrototype(name);
+ }
+
+ public boolean isTypeMatch(String name, Class> targetType) throws NoSuchBeanDefinitionException {
+ return this.beanFactory.isTypeMatch(name, targetType);
+ }
+
+ public Class> getType(String name) throws NoSuchBeanDefinitionException {
+ return this.beanFactory.getType(name);
+ }
+
+ public String[] getAliases(String name) {
+ return this.beanFactory.getAliases(name);
+ }
+
+ //---------------------------------------------------------------------
+ // Implementation of ListableBeanFactory interface
+ //---------------------------------------------------------------------
+
+ public boolean containsBeanDefinition(String beanName) {
+ return this.beanFactory.containsBeanDefinition(beanName);
+ }
+
+ public int getBeanDefinitionCount() {
+ return this.beanFactory.getBeanDefinitionCount();
+ }
+
+ public String[] getBeanDefinitionNames() {
+ return this.beanFactory.getBeanDefinitionNames();
+ }
+
+ public String[] getBeanNamesForType(Class> type) {
+ return this.beanFactory.getBeanNamesForType(type);
+ }
+
+ public String[] getBeanNamesForType(Class> type, boolean includeNonSingletons, boolean allowEagerInit) {
+ return this.beanFactory.getBeanNamesForType(type, includeNonSingletons, allowEagerInit);
+ }
+
+ public Map getBeansOfType(Class type) throws BeansException {
+ return this.beanFactory.getBeansOfType(type);
+ }
+
+ public Map getBeansOfType(Class type, boolean includeNonSingletons, boolean allowEagerInit)
+ throws BeansException {
+
+ return this.beanFactory.getBeansOfType(type, includeNonSingletons, allowEagerInit);
+ }
+
+ public Map getBeansWithAnnotation(Class extends Annotation> annotationType)
+ throws BeansException {
+
+ return this.beanFactory.getBeansWithAnnotation(annotationType);
+ }
+
+ public A findAnnotationOnBean(String beanName, Class annotationType) {
+ return this.beanFactory.findAnnotationOnBean(beanName, annotationType);
+ }
+
+ //---------------------------------------------------------------------
+ // Implementation of HierarchicalBeanFactory interface
+ //---------------------------------------------------------------------
+
+ public BeanFactory getParentBeanFactory() {
+ return null;
+ }
+
+ public boolean containsLocalBean(String name) {
+ return this.beanFactory.containsBean(name);
+ }
+
+ //---------------------------------------------------------------------
+ // Implementation of MessageSource interface
+ //---------------------------------------------------------------------
+
+ public String getMessage(String code, Object args[], String defaultMessage, Locale locale) {
+ return this.messageSource.getMessage(code, args, defaultMessage, locale);
+ }
+
+ public String getMessage(String code, Object args[], Locale locale) throws NoSuchMessageException {
+ return this.messageSource.getMessage(code, args, locale);
+ }
+
+ public String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException {
+ return this.messageSource.getMessage(resolvable, locale);
+ }
+
+ //---------------------------------------------------------------------
+ // Implementation of ResourceLoader interface
+ //---------------------------------------------------------------------
+
+ public ClassLoader getClassLoader() {
+ return null;
+ }
+
+ public Resource getResource(String location) {
+ return this.resourcePatternResolver.getResource(location);
+ }
+
+ //---------------------------------------------------------------------
+ // Other
+ //---------------------------------------------------------------------
+
+ public void publishEvent(ApplicationEvent event) {
+ }
+
+ 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 {
+
+ public Object initializeBean(Object existingBean, String beanName) throws BeansException {
+ if (existingBean instanceof ApplicationContextAware) {
+ ((ApplicationContextAware) existingBean).setApplicationContext(MockWebApplicationContext.this);
+ }
+ return existingBean;
+ }
+
+ public T createBean(Class beanClass) throws BeansException {
+ throw new UnsupportedOperationException("Bean creation is not supported");
+ }
+
+ @SuppressWarnings("rawtypes")
+ public Object createBean(Class beanClass, int autowireMode, boolean dependencyCheck) throws BeansException {
+ throw new UnsupportedOperationException("Bean creation is not supported");
+ }
+
+ @SuppressWarnings("rawtypes")
+ public Object autowire(Class beanClass, int autowireMode, boolean dependencyCheck) throws BeansException {
+ return null;
+ }
+
+ public void autowireBean(Object existingBean) throws BeansException {
+ throw new UnsupportedOperationException("Autowiring is not supported");
+ }
+
+ public void autowireBeanProperties(Object existingBean, int autowireMode, boolean dependencyCheck) throws BeansException {
+ throw new UnsupportedOperationException("Autowiring is not supported");
+ }
+
+ public Object configureBean(Object existingBean, String beanName) throws BeansException {
+ throw new UnsupportedOperationException("Configuring a bean is not supported");
+ }
+
+ public Object resolveDependency(DependencyDescriptor descriptor, String beanName) throws BeansException {
+ throw new UnsupportedOperationException("Dependency resolution is not supported");
+ }
+
+ public Object resolveDependency(DependencyDescriptor descriptor, String beanName, Set autowiredBeanNames,
+ TypeConverter typeConverter) throws BeansException {
+ throw new UnsupportedOperationException("Dependency resolution is not supported");
+ }
+
+ public void applyBeanPropertyValues(Object existingBean, String beanName) throws BeansException {
+ throw new UnsupportedOperationException("Bean property initialization is not supported");
+ }
+
+ public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
+ throws BeansException {
+ throw new UnsupportedOperationException("Post processing is not supported");
+ }
+
+ public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
+ throws BeansException {
+ throw new UnsupportedOperationException("Post processing is not supported");
+ }
+ }
+}
diff --git a/src/main/java/org/springframework/test/web/server/setup/PatternMappingFilterProxy.java b/src/main/java/org/springframework/test/web/server/setup/PatternMappingFilterProxy.java
new file mode 100644
index 0000000..5ca0aa6
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/server/setup/PatternMappingFilterProxy.java
@@ -0,0 +1,129 @@
+/*
+ * 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.server.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
+ */
+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 exactMatches = new ArrayList();
+
+ /** Patterns that require the URL to have a specific prefix, e.g. "/test/*" */
+ private final List startsWithMatches = new ArrayList();
+
+ /** Patterns that require the request URL to have a specific suffix, e.g. "*.html" */
+ private final List endsWithMatches = new ArrayList();
+
+
+ /**
+ * 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);
+ }
+ }
+
+ 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;
+ }
+
+ public void init(FilterConfig filterConfig) throws ServletException {
+ this.delegate.init(filterConfig);
+ }
+
+ public void destroy() {
+ this.delegate.destroy();
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/springframework/test/web/server/setup/StandaloneMockMvcBuilder.java b/src/main/java/org/springframework/test/web/server/setup/StandaloneMockMvcBuilder.java
index 2eb0ecd..8a5c6da 100644
--- a/src/main/java/org/springframework/test/web/server/setup/StandaloneMockMvcBuilder.java
+++ b/src/main/java/org/springframework/test/web/server/setup/StandaloneMockMvcBuilder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2011 the original author or authors.
+ * Copyright 2011-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.
@@ -21,205 +21,380 @@
import java.util.Collections;
import java.util.List;
import java.util.Locale;
-import java.util.Map;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import javax.servlet.ServletContext;
-import org.springframework.context.ApplicationContextAware;
+import org.springframework.beans.BeanUtils;
+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.test.web.server.AbstractMockMvcBuilder;
-import org.springframework.test.web.server.MockMvc;
import org.springframework.util.Assert;
+import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.validation.Validator;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
import org.springframework.web.context.WebApplicationContext;
-import org.springframework.web.context.support.GenericWebApplicationContext;
-import org.springframework.web.servlet.HandlerAdapter;
+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.HandlerMapping;
import org.springframework.web.servlet.LocaleResolver;
-import org.springframework.web.servlet.RequestToViewNameTranslator;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;
+import org.springframework.web.servlet.config.annotation.EnableWebMvc;
+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.MappedInterceptor;
import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver;
-import org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver;
-import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
-import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;
-import org.springframework.web.servlet.view.BeanNameViewResolver;
+import org.springframework.web.servlet.theme.FixedThemeResolver;
import org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator;
+import org.springframework.web.servlet.view.InternalResourceViewResolver;
/**
- * Builds a {@link MockMvc} by instantiating the required Spring MVC components directly rather than detecting
- * them in a Spring ApplicationContext. This makes it possible to build more "lightweight" and very focused tests
- * involving one or just a few controllers.
- *
- * The resulting setup is geared at supporting controllers with @{@link RequestMapping} methods. View resolution
- * can be configured by providing a list of {@link ViewResolver}s. When view resolution is left not configured, a
- * fixed, no-op {@link View} is used effectively ignoring rendering.
- *
- */
-public class StandaloneMockMvcBuilder extends AbstractMockMvcBuilder {
-
+ * A MockMvcBuilder that can be configured with controller instances 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.
+ *
+ *
+ * This builder creates the minimum infrastructure required by the
+ * {@link DispatcherServlet} to serve requests with annotated controllers and
+ * also provides various methods to customize it. The resulting configuration
+ * and customizations possible are equivalent to using the {@link EnableWebMvc
+ * MVC Java config} except with builder style methods rather than the callback.
+ *
+ *
+ * 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
+ */
+public class StandaloneMockMvcBuilder extends AbstractMockMvcBuilder {
+
private final Object[] controllers;
-
- private List> messageConverters;
-
- private Validator validator;
-
- private FormattingConversionService conversionService = new DefaultFormattingConversionService();
-
+
+ private List> messageConverters = new ArrayList>();
+
+ private List customArgumentResolvers = new ArrayList();
+
+ private List customReturnValueHandlers = new ArrayList();
+
private final List mappedInterceptors = new ArrayList();
- private List extends ViewResolver> viewResolvers;
+ private Validator validator = null;
+
+ private FormattingConversionService conversionService = null;
+
+ private List handlerExceptionResolvers = new ArrayList();
+
+ private List viewResolvers;
+
+ private LocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
+
+ private FlashMapManager flashMapManager = null;
+
+ private boolean useSuffixPatternMatch = true;
- private GenericWebApplicationContext applicationContext;
+ private boolean useTrailingSlashPatternMatch = true;
- protected StandaloneMockMvcBuilder(Object[] controllers) {
+
+ /**
+ * Protected constructor. Not intended for direct instantiation.
+ * @see MockMvcBuilders#standaloneSetup(Object...)
+ */
+ protected StandaloneMockMvcBuilder(Object... controllers) {
Assert.isTrue(!ObjectUtils.isEmpty(controllers), "At least one controller is required");
this.controllers = controllers;
- this.applicationContext = new GenericWebApplicationContext(new MockServletContext());
- this.applicationContext.refresh();
}
+ /**
+ * 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;
- this.applicationContext.getAutowireCapableBeanFactory().initializeBean(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) {
- mapInterceptors(null, interceptors);
+ addMappedInterceptors(null, interceptors);
return this;
}
- public StandaloneMockMvcBuilder mapInterceptors(String[] pathPatterns, HandlerInterceptor... interceptors) {
+ /**
+ * Add interceptors mapped to a set of path patterns.
+ */
+ public StandaloneMockMvcBuilder addMappedInterceptors(String[] pathPatterns, HandlerInterceptor... interceptors) {
for (HandlerInterceptor interceptor : interceptors) {
- mappedInterceptors.add(new MappedInterceptor(pathPatterns, interceptor));
+ this.mappedInterceptors.add(new MappedInterceptor(pathPatterns, interceptor));
}
return this;
}
-
+
+ /**
+ * Provide custom resolvers for controller method arguments.
+ */
+ public StandaloneMockMvcBuilder setCustomArgumentResolvers(HandlerMethodArgumentResolver... argumentResolvers) {
+ this.customArgumentResolvers = Arrays.asList(argumentResolvers);
+ return this;
+ }
+
/**
- * Configures a single ViewResolver that always renders using the provided View implementation.
- * Provides a simple way to render generated content (e.g. JSON, XML, Atom, etc.) For URL-based view types,
- * i.e. sub-classes of AbstractUrlBasedView, use {@link #setViewResolvers(ViewResolver...)} instead.
- *
- * @param view the default View to return for any view name
+ * Provide custom handlers for controller method return values.
*/
- public StandaloneMockMvcBuilder configureFixedViewResolver(View view) {
- viewResolvers = Collections.singletonList(new FixedViewResolver(view));
+ public StandaloneMockMvcBuilder setCustomReturnValueHandlers(HandlerMethodReturnValueHandler... handlers) {
+ this.customReturnValueHandlers = Arrays.asList(handlers);
return this;
}
+
/**
- * Configures view resolution with the given {@link ViewResolver}s.
- * By default, if no ViewResolvers have been configured, a View that doesn't do anything is used.
- *
- * Most ViewResolver types should work as expected. This excludes {@link BeanNameViewResolver}
- * since there is no ApplicationContext.
- *
+ * Set the HandlerExceptionResolver types to use.
+ */
+ public void setHandlerExceptionResolvers(List exceptionResolvers) {
+ this.handlerExceptionResolvers = exceptionResolvers;
+ }
+
+ /**
+ * 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) {
- viewResolvers = Arrays.asList(resolvers);
+ this.viewResolvers = Arrays.asList(resolvers);
return this;
}
-
- @Override
- protected WebApplicationContext initApplicationContext() {
- return applicationContext;
+
+ /**
+ * 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.singletonList(new StaticViewResolver(view));
+ return this;
}
- @Override
- protected List extends HandlerMapping> initHandlerMappings() {
- StaticRequestMappingHandlerMapping mapping = new StaticRequestMappingHandlerMapping();
- mapping.registerHandlers(controllers);
- mapping.setInterceptors(mappedInterceptors.toArray());
- return Collections.singletonList(mapping);
+ /**
+ * 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.*".
+ * 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/".
+ *
The default value is {@code true}.
+ */
+ public StandaloneMockMvcBuilder setUseTrailingSlashPatternMatch(boolean useTrailingSlashPatternMatch) {
+ this.useTrailingSlashPatternMatch = useTrailingSlashPatternMatch;
+ return this;
}
@Override
- protected List extends HandlerAdapter> initHandlerAdapters() {
- RequestMappingHandlerAdapter adapter = new RequestMappingHandlerAdapter();
- if (messageConverters != null) {
- adapter.setMessageConverters(messageConverters);
+ protected WebApplicationContext initWebApplicationContext() {
+ ServletContext servletContext = new MockServletContext();
+ MockWebApplicationContext cxt = new MockWebApplicationContext(servletContext);
+ registerMvcSingletons(cxt);
+ servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, cxt);
+ return cxt;
+ }
+
+ private void registerMvcSingletons(MockWebApplicationContext cxt) {
+
+ StandaloneConfiguration configuration = new StandaloneConfiguration();
+
+ RequestMappingHandlerMapping handlerMapping = configuration.requestMappingHandlerMapping();
+ handlerMapping.setServletContext(cxt.getServletContext());
+ handlerMapping.setApplicationContext(cxt);
+ cxt.addBean("requestMappingHandlerMapping", handlerMapping);
+
+ RequestMappingHandlerAdapter handlerAdapter = configuration.requestMappingHandlerAdapter();
+ handlerAdapter.setServletContext(cxt.getServletContext());
+ handlerAdapter.setApplicationContext(cxt);
+ handlerAdapter.afterPropertiesSet();
+ cxt.addBean("requestMappingHandlerAdapter", handlerAdapter);
+
+ cxt.addBean("handlerExceptionResolver", configuration.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());
+
+ initFlashMapManager();
+ cxt.addBean(DispatcherServlet.FLASH_MAP_MANAGER_BEAN_NAME, this.flashMapManager);
+ }
+
+ // TODO: remove in 3.2
+
+ private void initFlashMapManager() {
+ if (this.flashMapManager == null) {
+ String className = "org.springframework.web.servlet.support.DefaultFlashMapManager";
+ if (ClassUtils.isPresent(className, getClass().getClassLoader())) {
+ this.flashMapManager = instantiateClass(className);
+ }
+ else {
+ className = "org.springframework.web.servlet.support.SessionFlashMapManager";
+ this.flashMapManager = instantiateClass(className);
+ }
}
-
- ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer();
- initializer.setConversionService(conversionService);
- initializer.setValidator(validator);
- adapter.setWebBindingInitializer(initializer);
-
- adapter.setApplicationContext(applicationContext); // for SpEL expressions in annotations
- adapter.afterPropertiesSet();
-
- return Collections.singletonList(adapter);
}
- @Override
- protected List extends HandlerExceptionResolver> initHandlerExceptionResolvers() {
- ExceptionHandlerExceptionResolver exceptionResolver = new ExceptionHandlerExceptionResolver();
- if (messageConverters != null) {
- exceptionResolver.setMessageConverters( messageConverters);
+ @SuppressWarnings("unchecked")
+ private T instantiateClass(String className) {
+ Class> clazz;
+ try {
+ clazz = ClassUtils.forName(className, StandaloneMockMvcBuilder.class.getClassLoader());
+ }
+ catch (ClassNotFoundException e) {
+ throw new BeanInitializationException("Could not instantiate " + className, e);
}
- exceptionResolver.afterPropertiesSet();
-
- List resolvers = new ArrayList();
- resolvers.add(exceptionResolver);
- resolvers.add(new ResponseStatusExceptionResolver());
- resolvers.add(new DefaultHandlerExceptionResolver());
-
- return resolvers;
+ catch (LinkageError e) {
+ throw new BeanInitializationException("Could not instantiate " + className, e);
+ }
+ return (T) BeanUtils.instantiate(clazz);
}
- @Override
- protected List extends ViewResolver> initViewResolvers() {
- viewResolvers = (viewResolvers == null) ?
- Arrays.asList(new FixedViewResolver(NOOP_VIEW)) : viewResolvers;
-
- for (Object vr : viewResolvers) {
- if (vr instanceof ApplicationContextAware) {
- ((ApplicationContextAware) vr).setApplicationContext(applicationContext);
+ private List initViewResolvers(WebApplicationContext wac) {
+
+ this.viewResolvers = (this.viewResolvers == null) ?
+ Arrays.asList(new InternalResourceViewResolver()) : viewResolvers;
+
+ for (Object viewResolver : this.viewResolvers) {
+ if (viewResolver instanceof WebApplicationObjectSupport) {
+ ((WebApplicationObjectSupport) viewResolver).setApplicationContext(wac);
}
}
- return viewResolvers;
+ return this.viewResolvers;
}
- @Override
- protected RequestToViewNameTranslator initViewNameTranslator() {
- return new DefaultRequestToViewNameTranslator();
- }
- @Override
- protected LocaleResolver initLocaleResolver() {
- return new AcceptHeaderLocaleResolver();
+ /** Using the MVC Java configuration as the starting point for the "standalone" setup */
+ private class StandaloneConfiguration extends WebMvcConfigurationSupport {
+
+ @Override
+ public RequestMappingHandlerMapping requestMappingHandlerMapping() {
+
+ StaticRequestMappingHandlerMapping handlerMapping = new StaticRequestMappingHandlerMapping();
+ handlerMapping.registerHandlers(controllers);
+
+ handlerMapping.setUseSuffixPatternMatch(useSuffixPatternMatch);
+ handlerMapping.setUseTrailingSlashMatch(useTrailingSlashPatternMatch);
+ handlerMapping.setOrder(0);
+ handlerMapping.setInterceptors(getInterceptors());
+
+ return handlerMapping;
+ }
+
+ @Override
+ protected void configureMessageConverters(List> converters) {
+ converters.addAll(messageConverters);
+ }
+
+ @Override
+ protected void addArgumentResolvers(List argumentResolvers) {
+ argumentResolvers.addAll(customArgumentResolvers);
+ }
+
+ @Override
+ protected void addReturnValueHandlers(List 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 FormattingConversionService mvcConversionService() {
+ return (conversionService != null) ? conversionService : super.mvcConversionService();
+ }
+
+ @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 exceptionResolvers) {
+ exceptionResolvers.addAll(StandaloneMockMvcBuilder.this.handlerExceptionResolvers);
+ }
}
- /**
- * Allows registering controller instances.
- */
+ /** 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);
@@ -227,34 +402,18 @@ public void registerHandlers(Object...handlers) {
}
}
- /**
- * Resolves all view names to the same fixed View.
- */
- private static class FixedViewResolver implements ViewResolver {
-
+ /** A {@link ViewResolver} that always returns same View */
+ private static class StaticViewResolver implements ViewResolver {
+
private final View view;
-
- public FixedViewResolver(View view) {
+
+ public StaticViewResolver(View view) {
this.view = view;
}
public View resolveViewName(String viewName, Locale locale) throws Exception {
- return view;
+ return this.view;
}
}
-
- /**
- * A View implementation that doesn't do anything.
- */
- private static final View NOOP_VIEW = new View() {
-
- public String getContentType() {
- return null;
- }
-
- public void render(Map model, HttpServletRequest request, HttpServletResponse response)
- throws Exception {
- }
- };
}
diff --git a/src/main/java/org/springframework/test/web/server/setup/package-info.java b/src/main/java/org/springframework/test/web/server/setup/package-info.java
new file mode 100644
index 0000000..2eaa4a2
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/server/setup/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2011-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.server.setup.MockMvcBuilders}
+ * to access to instances of those implementations.
+ */
+package org.springframework.test.web.server.setup;
diff --git a/src/main/java/org/springframework/test/web/support/JsonPathExpectationsHelper.java b/src/main/java/org/springframework/test/web/support/JsonPathExpectationsHelper.java
new file mode 100644
index 0000000..2a0a891
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/support/JsonPathExpectationsHelper.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2011 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.support;
+
+import static org.springframework.test.web.AssertionErrors.assertTrue;
+
+import java.text.ParseException;
+import java.util.List;
+
+import org.hamcrest.Matcher;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+
+import com.jayway.jsonpath.InvalidPathException;
+import com.jayway.jsonpath.JsonPath;
+
+/**
+ * A helper class for applying assertions via JSONPath expressions.
+ *
+ * @author Rossen Stoyanchev
+ */
+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}.
+ */
+ @SuppressWarnings("unchecked")
+ public void assertValue(String content, Matcher matcher) throws ParseException {
+ T value = (T) evaluateJsonPath(content);
+ MatcherAssert.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(Object value) throws ParseException {
+ assertValue(Matchers.equalTo(value));
+ }
+
+ /**
+ * 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);
+ }
+ }
+
+}
diff --git a/src/main/java/org/springframework/test/web/support/XmlExpectationsHelper.java b/src/main/java/org/springframework/test/web/support/XmlExpectationsHelper.java
new file mode 100644
index 0000000..aa191f1
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/support/XmlExpectationsHelper.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2011 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.support;
+
+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.hamcrest.MatcherAssert;
+import org.springframework.test.web.AssertionErrors;
+import org.springframework.test.web.server.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
+ */
+public class XmlExpectationsHelper {
+
+
+ /**
+ * Parse the content as {@link Node} and apply a {@link Matcher}.
+ * @see org.hamcrest.Matchers#hasXPath
+ */
+ public void assertNode(String content, Matcher super Node> matcher) throws Exception {
+ Document document = parseXmlString(content);
+ MatcherAssert.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 xml-matchers
+ */
+ public void assertSource(String content, Matcher super Source> matcher) throws Exception {
+ Document document = parseXmlString(content);
+ MatcherAssert.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.
+ *
+ * Use of this method assumes the
+ * XMLUnit 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());
+ }
+ }
+
+}
diff --git a/src/main/java/org/springframework/test/web/support/XpathExpectationsHelper.java b/src/main/java/org/springframework/test/web/support/XpathExpectationsHelper.java
new file mode 100644
index 0000000..1e5e2c1
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/support/XpathExpectationsHelper.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright 2011-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.support;
+
+import static org.springframework.test.web.AssertionErrors.assertEquals;
+
+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.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+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;
+
+/**
+ * A helper class for applying assertions via XPath expressions.
+ *
+ * @author Rossen Stoyanchev
+ */
+public class XpathExpectationsHelper {
+
+ private final String expression;
+
+ private final XPathExpression xpathExpression;
+
+
+ /**
+ * 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 namespaces, Object... args)
+ throws XPathExpressionException {
+
+ this.expression = String.format(expression, args);
+ this.xpathExpression = compileXpathExpression(this.expression, namespaces);
+ }
+
+ private XPathExpression compileXpathExpression(String expression, Map namespaces)
+ throws XPathExpressionException {
+
+ SimpleNamespaceContext namespaceContext = new SimpleNamespaceContext();
+ namespaceContext.setBindings((namespaces != null) ? namespaces : Collections. 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}.
+ */
+ public void assertNode(String content, final Matcher super Node> matcher) throws Exception {
+ Document document = parseXmlString(content);
+ Node node = evaluateXpath(document, XPathConstants.NODE, Node.class);
+ MatcherAssert.assertThat("Xpath: " + XpathExpectationsHelper.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(true);
+ 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 evaluateXpath(Document document, QName evaluationType, Class 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 {
+ assertNode(content, Matchers.notNullValue());
+ }
+
+ /**
+ * 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 {
+ assertNode(content, Matchers.nullValue());
+ }
+
+ /**
+ * 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 matcher) throws Exception {
+ Document document = parseXmlString(content);
+ NodeList nodeList = evaluateXpath(document, XPathConstants.NODESET, NodeList.class);
+ String reason = "nodeCount Xpath: " + XpathExpectationsHelper.this.expression;
+ MatcherAssert.assertThat(reason, 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 {
+ assertNodeCount(content, Matchers.equalTo(expectedCount));
+ }
+
+ /**
+ * 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);
+ MatcherAssert.assertThat("Xpath: " + XpathExpectationsHelper.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 {
+ assertString(content, Matchers.equalTo(expectedValue));
+ }
+
+ /**
+ * 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);
+ MatcherAssert.assertThat("Xpath: " + XpathExpectationsHelper.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 {
+ assertNumber(content, Matchers.equalTo(expectedValue));
+ }
+
+ /**
+ * 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 result = evaluateXpath(document, XPathConstants.STRING, String.class);
+ assertEquals("Xpath:", expectedValue, Boolean.parseBoolean(result));
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/springframework/test/web/support/package-info.java b/src/main/java/org/springframework/test/web/support/package-info.java
new file mode 100644
index 0000000..43dcbf3
--- /dev/null
+++ b/src/main/java/org/springframework/test/web/support/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2011-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.
+ */
+
+/**
+ * Support classes shared among client and server-side Spring MVC Test classes.
+ */
+package org.springframework.test.web.support;
diff --git a/src/test/java/org/springframework/test/web/Person.java b/src/test/java/org/springframework/test/web/Person.java
new file mode 100644
index 0000000..17008b2
--- /dev/null
+++ b/src/test/java/org/springframework/test/web/Person.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2011 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;
+
+import javax.validation.constraints.NotNull;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.springframework.util.ObjectUtils;
+
+@XmlRootElement
+public class Person {
+
+ @NotNull
+ private String name;
+
+ private double someDouble;
+
+ private boolean someBoolean;
+
+ public Person() {
+ }
+
+ public Person(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Person setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ public double getSomeDouble() {
+ return someDouble;
+ }
+
+ public Person setSomeDouble(double someDouble) {
+ this.someDouble = someDouble;
+ return this;
+ }
+
+ public boolean isSomeBoolean() {
+ return someBoolean;
+ }
+
+ public Person setSomeBoolean(boolean someBoolean) {
+ this.someBoolean = someBoolean;
+ return this;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (!(other instanceof Person)) {
+ return false;
+ }
+ Person otherPerson = (Person) other;
+ return (ObjectUtils.nullSafeEquals(this.name, otherPerson.name) &&
+ ObjectUtils.nullSafeEquals(this.someDouble, otherPerson.someDouble) &&
+ ObjectUtils.nullSafeEquals(this.someBoolean, otherPerson.someBoolean));
+ }
+
+ @Override
+ public String toString() {
+ return "Person [name=" + this.name + ", someDouble=" + this.someDouble
+ + ", someBoolean=" + this.someBoolean + "]";
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/springframework/test/web/client/MockClientHttpRequestFactoryTests.java b/src/test/java/org/springframework/test/web/client/MockClientHttpRequestFactoryTests.java
new file mode 100644
index 0000000..6e1fbae
--- /dev/null
+++ b/src/test/java/org/springframework/test/web/client/MockClientHttpRequestFactoryTests.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2011-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.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.springframework.test.web.client.match.RequestMatchers.anything;
+
+import java.net.URI;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.client.ClientHttpRequest;
+import org.springframework.http.client.ClientHttpRequestFactory;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * Tests for {@link MockClientHttpRequestFactory}.
+ *
+ * @author Rossen Stoyanchev
+ */
+public class MockClientHttpRequestFactoryTests {
+
+ private MockRestServiceServer server;
+
+ private ClientHttpRequestFactory factory;
+
+
+ @Before
+ public void setup() {
+ RestTemplate restTemplate = new RestTemplate();
+ this.server = MockRestServiceServer.createServer(restTemplate);
+ this.factory = restTemplate.getRequestFactory();
+ }
+
+ @Test
+ public void createRequest() throws Exception {
+ URI uri = new URI("/foo");
+ ClientHttpRequest expected = (ClientHttpRequest) this.server.expect(anything());
+ ClientHttpRequest actual = this.factory.createRequest(uri, HttpMethod.GET);
+
+ assertSame(expected, actual);
+ assertEquals(uri, actual.getURI());
+ assertEquals(HttpMethod.GET, actual.getMethod());
+ }
+
+ @Test
+ public void noFurtherRequestsExpected() throws Exception {
+ try {
+ this.factory.createRequest(new URI("/foo"), HttpMethod.GET);
+ }
+ catch (AssertionError error) {
+ assertEquals("No further requests expected", error.getMessage());
+ }
+ }
+
+ @Test
+ public void verifyZeroExpected() throws Exception {
+ this.server.verify();
+ }
+
+ @Test
+ public void verifyExpectedEqualExecuted() throws Exception {
+ this.server.expect(anything());
+ this.server.expect(anything());
+
+ this.factory.createRequest(new URI("/foo"), HttpMethod.GET);
+ this.factory.createRequest(new URI("/bar"), HttpMethod.POST);
+ }
+
+ @Test
+ public void verifyMoreExpected() throws Exception {
+ this.server.expect(anything());
+ this.server.expect(anything());
+
+ this.factory.createRequest(new URI("/foo"), HttpMethod.GET);
+
+ try {
+ this.server.verify();
+ }
+ catch (AssertionError error) {
+ assertTrue(error.getMessage(), error.getMessage().contains("1 out of 2 were executed"));
+ }
+ }
+
+}
diff --git a/src/test/java/org/springframework/test/web/client/match/ContentRequestMatchersTests.java b/src/test/java/org/springframework/test/web/client/match/ContentRequestMatchersTests.java
new file mode 100644
index 0000000..3018bdf
--- /dev/null
+++ b/src/test/java/org/springframework/test/web/client/match/ContentRequestMatchersTests.java
@@ -0,0 +1,121 @@
+/*
+ * 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.hamcrest.Matchers.hasXPath;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.http.MediaType;
+import org.springframework.mock.http.client.MockClientHttpRequest;
+
+/**
+ * Tests for {@link ContentRequestMatchers}.
+ *
+ * @author Rossen Stoyanchev
+ */
+public class ContentRequestMatchersTests {
+
+ private MockClientHttpRequest request;
+
+ @Before
+ public void setUp() {
+ this.request = new MockClientHttpRequest();
+ }
+
+ @Test
+ public void testContentType() throws Exception {
+ this.request.getHeaders().setContentType(MediaType.APPLICATION_JSON);
+
+ RequestMatchers.content().mimeType("application/json").match(this.request);
+ RequestMatchers.content().mimeType(MediaType.APPLICATION_JSON).match(this.request);
+ }
+
+ @Test(expected=AssertionError.class)
+ public void testContentTypeNoMatch1() throws Exception {
+ this.request.getHeaders().setContentType(MediaType.APPLICATION_JSON);
+
+ RequestMatchers.content().mimeType("application/xml").match(this.request);
+ }
+
+ @Test(expected=AssertionError.class)
+ public void testContentTypeNoMatch2() throws Exception {
+ this.request.getHeaders().setContentType(MediaType.APPLICATION_JSON);
+
+ RequestMatchers.content().mimeType(MediaType.APPLICATION_ATOM_XML).match(this.request);
+ }
+
+ @Test
+ public void testString() throws Exception {
+ this.request.getBody().write("test".getBytes());
+
+ RequestMatchers.content().string("test").match(this.request);
+ }
+
+ @Test(expected=AssertionError.class)
+ public void testStringNoMatch() throws Exception {
+ this.request.getBody().write("test".getBytes());
+
+ RequestMatchers.content().string("Test").match(this.request);
+ }
+
+ @Test
+ public void testBytes() throws Exception {
+ byte[] content = "test".getBytes();
+ this.request.getBody().write(content);
+
+ RequestMatchers.content().bytes(content).match(this.request);
+ }
+
+ @Test(expected=AssertionError.class)
+ public void testBytesNoMatch() throws Exception {
+ this.request.getBody().write("test".getBytes());
+
+ RequestMatchers.content().bytes("Test".getBytes()).match(this.request);
+ }
+
+ @Test
+ public void testXml() throws Exception {
+ String content = "baz bazz ";
+ this.request.getBody().write(content.getBytes());
+
+ RequestMatchers.content().xml(content).match(this.request);
+ }
+
+ @Test(expected=AssertionError.class)
+ public void testXmlNoMatch() throws Exception {
+ this.request.getBody().write("11 ".getBytes());
+
+ RequestMatchers.content().xml("22 ").match(this.request);
+ }
+
+ @Test
+ public void testNodeMatcher() throws Exception {
+ String content = "baz ";
+ this.request.getBody().write(content.getBytes());
+
+ RequestMatchers.content().node(hasXPath("/foo/bar")).match(this.request);
+ }
+
+ @Test(expected=AssertionError.class)
+ public void testNodeMatcherNoMatch() throws Exception {
+ String content = "baz ";
+ this.request.getBody().write(content.getBytes());
+
+ RequestMatchers.content().node(hasXPath("/foo/bar/bar")).match(this.request);
+ }
+
+}
diff --git a/src/test/java/org/springframework/test/web/client/match/JsonPathRequestMatchersTests.java b/src/test/java/org/springframework/test/web/client/match/JsonPathRequestMatchersTests.java
new file mode 100644
index 0000000..e05a5c6
--- /dev/null
+++ b/src/test/java/org/springframework/test/web/client/match/JsonPathRequestMatchersTests.java
@@ -0,0 +1,92 @@
+/*
+ * 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 org.hamcrest.Matchers;
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.mock.http.client.MockClientHttpRequest;
+
+/**
+ * Tests for {@link JsonPathRequestMatchers}.
+ *
+ * @author Rossen Stoyanchev
+ */
+public class JsonPathRequestMatchersTests {
+
+ private static final String RESPONSE_CONTENT = "{\"foo\":\"bar\", \"qux\":[\"baz1\",\"baz2\"]}";
+
+ private MockClientHttpRequest request;
+
+ @Before
+ public void setUp() throws IOException {
+ this.request = new MockClientHttpRequest();
+ this.request.getBody().write(RESPONSE_CONTENT.getBytes());
+ }
+
+ @Test
+ public void value() throws Exception {
+ new JsonPathRequestMatchers("$.foo").value("bar").match(this.request);
+ }
+
+ @Test(expected=AssertionError.class)
+ public void valueNoMatch() throws Exception {
+ new JsonPathRequestMatchers("$.foo").value("bogus").match(this.request);
+ }
+
+ @Test
+ public void valueMatcher() throws Exception {
+ new JsonPathRequestMatchers("$.foo").value(Matchers.equalTo("bar")).match(this.request);
+ }
+
+ @Test(expected=AssertionError.class)
+ public void valueMatcherNoMatch() throws Exception {
+ new JsonPathRequestMatchers("$.foo").value(Matchers.equalTo("bogus")).match(this.request);
+ }
+
+ @Test
+ public void exists() throws Exception {
+ new JsonPathRequestMatchers("$.foo").exists().match(this.request);
+ }
+
+ @Test(expected=AssertionError.class)
+ public void existsNoMatch() throws Exception {
+ new JsonPathRequestMatchers("$.bogus").exists().match(this.request);
+ }
+
+ @Test
+ public void doesNotExist() throws Exception {
+ new JsonPathRequestMatchers("$.bogus").doesNotExist().match(this.request);
+ }
+
+ @Test(expected=AssertionError.class)
+ public void doesNotExistNoMatch() throws Exception {
+ new JsonPathRequestMatchers("$.foo").doesNotExist().match(this.request);
+ }
+
+ @Test
+ public void isArrayMatch() throws Exception {
+ new JsonPathRequestMatchers("$.qux").isArray().match(this.request);
+ }
+
+ @Test(expected=AssertionError.class)
+ public void isArrayNoMatch() throws Exception {
+ new JsonPathRequestMatchers("$.bar").isArray().match(this.request);
+ }
+
+}
diff --git a/src/test/java/org/springframework/test/web/client/match/RequestMatchersTests.java b/src/test/java/org/springframework/test/web/client/match/RequestMatchersTests.java
new file mode 100644
index 0000000..afca10d
--- /dev/null
+++ b/src/test/java/org/springframework/test/web/client/match/RequestMatchersTests.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2011-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.hamcrest.Matchers.containsString;
+
+import java.net.URI;
+import java.util.Arrays;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.http.HttpMethod;
+import org.springframework.mock.http.client.MockClientHttpRequest;
+
+/**
+ * Tests for {@link RequestMatchers}.
+ *
+ * @author Craig Walls
+ * @author Rossen Stoyanchev
+ */
+public class RequestMatchersTests {
+
+ private MockClientHttpRequest request;
+
+ @Before
+ public void setUp() {
+ this.request = new MockClientHttpRequest();
+ }
+
+ @Test
+ public void requestTo() throws Exception {
+ this.request.setURI(new URI("http://foo.com/bar"));
+
+ RequestMatchers.requestTo("http://foo.com/bar").match(this.request);
+ }
+
+ @Test(expected=AssertionError.class)
+ public void requestToNoMatch() throws Exception {
+ this.request.setURI(new URI("http://foo.com/bar"));
+
+ RequestMatchers.requestTo("http://foo.com/wrong").match(this.request);
+ }
+
+ @Test
+ public void requestToContains() throws Exception {
+ this.request.setURI(new URI("http://foo.com/bar"));
+
+ RequestMatchers.requestTo(containsString("bar")).match(this.request);
+ }
+
+ @Test
+ public void method() throws Exception {
+ this.request.setMethod(HttpMethod.GET);
+
+ RequestMatchers.method(HttpMethod.GET).match(this.request);
+ }
+
+ @Test(expected=AssertionError.class)
+ public void methodNoMatch() throws Exception {
+ this.request.setMethod(HttpMethod.POST);
+
+ RequestMatchers.method(HttpMethod.GET).match(this.request);
+ }
+
+ @Test
+ public void header() throws Exception {
+ this.request.getHeaders().put("foo", Arrays.asList("bar", "baz"));
+
+ RequestMatchers.header("foo", "bar", "baz").match(this.request);
+ }
+
+ @Test(expected=AssertionError.class)
+ public void headerMissing() throws Exception {
+ RequestMatchers.header("foo", "bar").match(this.request);
+ }
+
+ @Test(expected=AssertionError.class)
+ public void headerMissingValue() throws Exception {
+ this.request.getHeaders().put("foo", Arrays.asList("bar", "baz"));
+
+ RequestMatchers.header("foo", "bad").match(this.request);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void headerContains() throws Exception {
+ this.request.getHeaders().put("foo", Arrays.asList("bar", "baz"));
+
+ RequestMatchers.header("foo", containsString("ba")).match(this.request);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test(expected=AssertionError.class)
+ public void headerContainsWithMissingHeader() throws Exception {
+ RequestMatchers.header("foo", containsString("baz")).match(this.request);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test(expected=AssertionError.class)
+ public void headerContainsWithMissingValue() throws Exception {
+ this.request.getHeaders().put("foo", Arrays.asList("bar", "baz"));
+
+ RequestMatchers.header("foo", containsString("bx")).match(this.request);
+ }
+
+ @Test
+ public void headers() throws Exception {
+ this.request.getHeaders().put("foo", Arrays.asList("bar", "baz"));
+
+ RequestMatchers.header("foo", "bar", "baz").match(this.request);
+ }
+
+ @Test(expected=AssertionError.class)
+ public void headersWithMissingHeader() throws Exception {
+ RequestMatchers.header("foo", "bar").match(this.request);
+ }
+
+ @Test(expected=AssertionError.class)
+ public void headersWithMissingValue() throws Exception {
+ this.request.getHeaders().put("foo", Arrays.asList("bar"));
+
+ RequestMatchers.header("foo", "bar", "baz").match(this.request);
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/springframework/test/web/client/match/XpathRequestMatchersTests.java b/src/test/java/org/springframework/test/web/client/match/XpathRequestMatchersTests.java
new file mode 100644
index 0000000..823d220
--- /dev/null
+++ b/src/test/java/org/springframework/test/web/client/match/XpathRequestMatchersTests.java
@@ -0,0 +1,112 @@
+/*
+ * 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 org.hamcrest.Matchers;
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.mock.http.client.MockClientHttpRequest;
+
+/**
+ * Tests for {@link XpathRequestMatchers}.
+ *
+ * @author Rossen Stoyanchev
+ */
+public class XpathRequestMatchersTests {
+
+ private static final String RESPONSE_CONTENT = "111 true ";
+
+ private MockClientHttpRequest request;
+
+ @Before
+ public void setUp() throws IOException {
+ this.request = new MockClientHttpRequest();
+ this.request.getBody().write(RESPONSE_CONTENT.getBytes());
+ }
+
+ @Test
+ public void testNodeMatcher() throws Exception {
+ new XpathRequestMatchers("/foo/bar", null).node(Matchers.notNullValue()).match(this.request);
+ }
+
+ @Test(expected=AssertionError.class)
+ public void testNodeMatcherNoMatch() throws Exception {
+ new XpathRequestMatchers("/foo/bar", null).node(Matchers.nullValue()).match(this.request);
+ }
+
+ @Test
+ public void testExists() throws Exception {
+ new XpathRequestMatchers("/foo/bar", null).exists().match(this.request);
+ }
+
+ @Test(expected=AssertionError.class)
+ public void testExistsNoMatch() throws Exception {
+ new XpathRequestMatchers("/foo/Bar", null).exists().match(this.request);
+ }
+
+ @Test
+ public void testDoesNotExist() throws Exception {
+ new XpathRequestMatchers("/foo/Bar", null).doesNotExist().match(this.request);
+ }
+
+ @Test(expected=AssertionError.class)
+ public void testDoesNotExistNoMatch() throws Exception {
+ new XpathRequestMatchers("/foo/bar", null).doesNotExist().match(this.request);
+ }
+
+ @Test
+ public void testNodeCount() throws Exception {
+ new XpathRequestMatchers("/foo/bar", null).nodeCount(2).match(this.request);
+ }
+
+ @Test(expected=AssertionError.class)
+ public void testNodeCountNoMatch() throws Exception {
+ new XpathRequestMatchers("/foo/bar", null).nodeCount(1).match(this.request);
+ }
+
+ @Test
+ public void testString() throws Exception {
+ new XpathRequestMatchers("/foo/bar[1]", null).string("111").match(this.request);
+ }
+
+ @Test(expected=AssertionError.class)
+ public void testStringNoMatch() throws Exception {
+ new XpathRequestMatchers("/foo/bar[1]", null).string("112").match(this.request);
+ }
+
+ @Test
+ public void testNumber() throws Exception {
+ new XpathRequestMatchers("/foo/bar[1]", null).number(111.0).match(this.request);
+ }
+
+ @Test(expected=AssertionError.class)
+ public void testNumberNoMatch() throws Exception {
+ new XpathRequestMatchers("/foo/bar[1]", null).number(111.1).match(this.request);
+ }
+
+ @Test
+ public void testBoolean() throws Exception {
+ new XpathRequestMatchers("/foo/bar[2]", null).booleanValue(true).match(this.request);
+ }
+
+ @Test(expected=AssertionError.class)
+ public void testBooleanNoMatch() throws Exception {
+ new XpathRequestMatchers("/foo/bar[2]", null).booleanValue(false).match(this.request);
+ }
+
+}
diff --git a/src/test/java/org/springframework/test/web/client/response/ResponseCreatorsTests.java b/src/test/java/org/springframework/test/web/client/response/ResponseCreatorsTests.java
new file mode 100644
index 0000000..dd90d58
--- /dev/null
+++ b/src/test/java/org/springframework/test/web/client/response/ResponseCreatorsTests.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright 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 static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.net.URI;
+
+import org.junit.Test;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.mock.http.client.MockClientHttpResponse;
+import org.springframework.util.FileCopyUtils;
+
+/**
+ * Tests for the {@link ResponseCreators} static factory methods.
+ *
+ * @author Rossen Stoyanchev
+ */
+public class ResponseCreatorsTests {
+
+ @Test
+ public void success() throws Exception {
+ MockClientHttpResponse response = (MockClientHttpResponse) ResponseCreators.withSuccess().createResponse(null);
+
+ assertEquals(HttpStatus.OK, response.getStatusCode());
+ assertTrue(response.getHeaders().isEmpty());
+ assertNull(response.getBody());
+ }
+
+ @Test
+ public void successWithContent() throws Exception {
+ DefaultResponseCreator responseCreator = ResponseCreators.withSuccess("foo", MediaType.TEXT_PLAIN);
+ MockClientHttpResponse response = (MockClientHttpResponse) responseCreator.createResponse(null);
+
+ assertEquals(HttpStatus.OK, response.getStatusCode());
+ assertEquals(MediaType.TEXT_PLAIN, response.getHeaders().getContentType());
+ assertArrayEquals("foo".getBytes(), FileCopyUtils.copyToByteArray(response.getBody()));
+ }
+
+ @Test
+ public void successWithContentWithoutContentType() throws Exception {
+ DefaultResponseCreator responseCreator = ResponseCreators.withSuccess("foo", null);
+ MockClientHttpResponse response = (MockClientHttpResponse) responseCreator.createResponse(null);
+
+ assertEquals(HttpStatus.OK, response.getStatusCode());
+ assertNull(response.getHeaders().getContentType());
+ assertArrayEquals("foo".getBytes(), FileCopyUtils.copyToByteArray(response.getBody()));
+ }
+
+ @Test
+ public void created() throws Exception {
+ URI location = new URI("/foo");
+ DefaultResponseCreator responseCreator = ResponseCreators.withCreatedEntity(location);
+ MockClientHttpResponse response = (MockClientHttpResponse) responseCreator.createResponse(null);
+
+ assertEquals(HttpStatus.CREATED, response.getStatusCode());
+ assertEquals(location, response.getHeaders().getLocation());
+ assertNull(response.getBody());
+ }
+
+ @Test
+ public void noContent() throws Exception {
+ DefaultResponseCreator responseCreator = ResponseCreators.withNoContent();
+ MockClientHttpResponse response = (MockClientHttpResponse) responseCreator.createResponse(null);
+
+ assertEquals(HttpStatus.NO_CONTENT, response.getStatusCode());
+ assertTrue(response.getHeaders().isEmpty());
+ assertNull(response.getBody());
+ }
+
+ @Test
+ public void badRequest() throws Exception {
+ DefaultResponseCreator responseCreator = ResponseCreators.withBadRequest();
+ MockClientHttpResponse response = (MockClientHttpResponse) responseCreator.createResponse(null);
+
+ assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());
+ assertTrue(response.getHeaders().isEmpty());
+ assertNull(response.getBody());
+ }
+
+ @Test
+ public void unauthorized() throws Exception {
+ DefaultResponseCreator responseCreator = ResponseCreators.withUnauthorizedRequest();
+ MockClientHttpResponse response = (MockClientHttpResponse) responseCreator.createResponse(null);
+
+ assertEquals(HttpStatus.UNAUTHORIZED, response.getStatusCode());
+ assertTrue(response.getHeaders().isEmpty());
+ assertNull(response.getBody());
+ }
+
+ @Test
+ public void serverError() throws Exception {
+ DefaultResponseCreator responseCreator = ResponseCreators.withServerError();
+ MockClientHttpResponse response = (MockClientHttpResponse) responseCreator.createResponse(null);
+
+ assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode());
+ assertTrue(response.getHeaders().isEmpty());
+ assertNull(response.getBody());
+ }
+
+ @Test
+ public void withStatus() throws Exception {
+ DefaultResponseCreator responseCreator = ResponseCreators.withStatus(HttpStatus.FORBIDDEN);
+ MockClientHttpResponse response = (MockClientHttpResponse) responseCreator.createResponse(null);
+
+ assertEquals(HttpStatus.FORBIDDEN, response.getStatusCode());
+ assertTrue(response.getHeaders().isEmpty());
+ assertNull(response.getBody());
+ }
+
+}
diff --git a/src/test/java/org/springframework/test/web/client/samples/SampleTests.java b/src/test/java/org/springframework/test/web/client/samples/SampleTests.java
new file mode 100644
index 0000000..fcc93a3
--- /dev/null
+++ b/src/test/java/org/springframework/test/web/client/samples/SampleTests.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2011-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.samples;
+
+import static org.junit.Assert.assertTrue;
+import static org.springframework.test.web.client.match.RequestMatchers.method;
+import static org.springframework.test.web.client.match.RequestMatchers.requestTo;
+import static org.springframework.test.web.client.response.ResponseCreators.withSuccess;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.Resource;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.Person;
+import org.springframework.test.web.client.MockRestServiceServer;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * Examples to demonstrate writing client-side REST tests with Spring MVC Test.
+ * While the tests in this class invoke the RestTemplate directly, in actual
+ * tests the RestTemplate may likely be invoked indirectly, i.e. through client
+ * code.
+ *
+ * @author Rossen Stoyanchev
+ */
+public class SampleTests {
+
+ private MockRestServiceServer mockServer;
+
+ private RestTemplate restTemplate;
+
+ @Before
+ public void setup() {
+ this.restTemplate = new RestTemplate();
+ this.mockServer = MockRestServiceServer.createServer(this.restTemplate);
+ }
+
+ @Test
+ public void performGet() throws Exception {
+
+ String responseBody = "{\"name\" : \"Ludwig van Beethoven\", \"someDouble\" : \"1.6035\"}";
+
+ this.mockServer.expect(requestTo("/composers/42")).andExpect(method(HttpMethod.GET))
+ .andRespond(withSuccess(responseBody, MediaType.APPLICATION_JSON));
+
+ @SuppressWarnings("unused")
+ Person ludwig = restTemplate.getForObject("/composers/{id}", Person.class, 42);
+
+ // person.getName().equals("Ludwig van Beethoven")
+ // person.getDouble().equals(1.6035)
+
+ this.mockServer.verify();
+ }
+
+ @Test
+ public void performGetWithResponseBodyFromFile() throws Exception {
+
+ Resource responseBody = new ClassPathResource("ludwig.json", this.getClass());
+
+ this.mockServer.expect(requestTo("/composers/42")).andExpect(method(HttpMethod.GET))
+ .andRespond(withSuccess(responseBody, MediaType.APPLICATION_JSON));
+
+ @SuppressWarnings("unused")
+ Person ludwig = restTemplate.getForObject("/composers/{id}", Person.class, 42);
+
+ // hotel.getId() == 42
+ // hotel.getName().equals("Holiday Inn")
+
+ this.mockServer.verify();
+ }
+
+ @Test
+ public void verify() {
+
+ this.mockServer.expect(requestTo("/number")).andExpect(method(HttpMethod.GET))
+ .andRespond(withSuccess("1", MediaType.TEXT_PLAIN));
+
+ this.mockServer.expect(requestTo("/number")).andExpect(method(HttpMethod.GET))
+ .andRespond(withSuccess("2", MediaType.TEXT_PLAIN));
+
+ this.mockServer.expect(requestTo("/number")).andExpect(method(HttpMethod.GET))
+ .andRespond(withSuccess("4", MediaType.TEXT_PLAIN));
+
+ this.mockServer.expect(requestTo("/number")).andExpect(method(HttpMethod.GET))
+ .andRespond(withSuccess("8", MediaType.TEXT_PLAIN));
+
+ @SuppressWarnings("unused")
+ String result = this.restTemplate.getForObject("/number", String.class);
+ // result == "1"
+
+ result = this.restTemplate.getForObject("/number", String.class);
+ // result == "2"
+
+ try {
+ this.mockServer.verify();
+ }
+ catch (AssertionError error) {
+ assertTrue(error.getMessage(), error.getMessage().contains("2 out of 4 were executed"));
+ }
+ }
+}
diff --git a/src/test/java/org/springframework/test/web/client/samples/matchers/ContentRequestMatcherTests.java b/src/test/java/org/springframework/test/web/client/samples/matchers/ContentRequestMatcherTests.java
new file mode 100644
index 0000000..5aea1f0
--- /dev/null
+++ b/src/test/java/org/springframework/test/web/client/samples/matchers/ContentRequestMatcherTests.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2011-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.samples.matchers;
+
+import static org.hamcrest.Matchers.startsWith;
+import static org.junit.Assert.assertTrue;
+import static org.springframework.test.web.client.match.RequestMatchers.content;
+import static org.springframework.test.web.client.response.ResponseCreators.withSuccess;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.converter.StringHttpMessageConverter;
+import org.springframework.http.converter.json.MappingJacksonHttpMessageConverter;
+import org.springframework.test.web.Person;
+import org.springframework.test.web.client.MockRestServiceServer;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * Examples of defining expectations on request content and content type.
+ *
+ * @author Rossen Stoyanchev
+ *
+ * @see JsonPathRequestMatcherTests
+ * @see XmlContentRequestMatcherTests
+ * @see XpathRequestMatcherTests
+ */
+public class ContentRequestMatcherTests {
+
+ private MockRestServiceServer mockServer;
+
+ private RestTemplate restTemplate;
+
+ @Before
+ public void setup() {
+ List> converters = new ArrayList>();
+ converters.add(new StringHttpMessageConverter());
+ converters.add(new MappingJacksonHttpMessageConverter());
+
+ this.restTemplate = new RestTemplate();
+ this.restTemplate.setMessageConverters(converters);
+
+ this.mockServer = MockRestServiceServer.createServer(this.restTemplate);
+ }
+
+ @Test
+ public void contentType() throws Exception {
+ this.mockServer.expect(content().mimeType("application/json;charset=UTF-8")).andRespond(withSuccess());
+ this.restTemplate.put(new URI("/foo"), new Person());
+ this.mockServer.verify();
+ }
+
+ @Test
+ public void contentTypeNoMatch() throws Exception {
+ this.mockServer.expect(content().mimeType("application/json;charset=UTF-8")).andRespond(withSuccess());
+ try {
+ this.restTemplate.put(new URI("/foo"), "foo");
+ }
+ catch (AssertionError error) {
+ String message = error.getMessage();
+ assertTrue(message, message.startsWith("Content type expected:"));
+ }
+ }
+
+ @Test
+ public void contentAsString() throws Exception {
+ this.mockServer.expect(content().string("foo")).andRespond(withSuccess());
+ this.restTemplate.put(new URI("/foo"), "foo");
+ this.mockServer.verify();
+ }
+
+ @Test
+ public void contentStringStartsWith() throws Exception {
+ this.mockServer.expect(content().string(startsWith("foo"))).andRespond(withSuccess());
+ this.restTemplate.put(new URI("/foo"), "foo123");
+ this.mockServer.verify();
+ }
+
+ @Test
+ public void contentAsBytes() throws Exception {
+ this.mockServer.expect(content().bytes("foo".getBytes())).andRespond(withSuccess());
+ this.restTemplate.put(new URI("/foo"), "foo");
+ this.mockServer.verify();
+ }
+
+}
diff --git a/src/test/java/org/springframework/test/web/client/samples/matchers/HeaderRequestMatcherTests.java b/src/test/java/org/springframework/test/web/client/samples/matchers/HeaderRequestMatcherTests.java
new file mode 100644
index 0000000..f9b55a8
--- /dev/null
+++ b/src/test/java/org/springframework/test/web/client/samples/matchers/HeaderRequestMatcherTests.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2011-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.samples.matchers;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.springframework.test.web.client.match.RequestMatchers.header;
+import static org.springframework.test.web.client.match.RequestMatchers.requestTo;
+import static org.springframework.test.web.client.response.ResponseCreators.withSuccess;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.converter.StringHttpMessageConverter;
+import org.springframework.http.converter.json.MappingJacksonHttpMessageConverter;
+import org.springframework.test.web.Person;
+import org.springframework.test.web.client.MockRestServiceServer;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * Examples of defining expectations on request headers.
+ *
+ * @author Rossen Stoyanchev
+ */
+public class HeaderRequestMatcherTests {
+
+ private static final String RESPONSE_BODY = "{\"name\" : \"Ludwig van Beethoven\", \"someDouble\" : \"1.6035\"}";
+
+ private MockRestServiceServer mockServer;
+
+ private RestTemplate restTemplate;
+
+ @Before
+ public void setup() {
+ List> converters = new ArrayList