diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/MockMvc.java b/spring-test/src/main/java/org/springframework/test/web/servlet/MockMvc.java index a25452dc800..33bbd406bb9 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/MockMvc.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/MockMvc.java @@ -134,6 +134,10 @@ public final class MockMvc { MockHttpServletRequest request = requestBuilder.buildRequest(this.servletContext); MockHttpServletResponse response = new MockHttpServletResponse(); + if (requestBuilder instanceof SmartRequestBuilder) { + request = ((SmartRequestBuilder) requestBuilder).postProcessRequest(request); + } + final MvcResult mvcResult = new DefaultMvcResult(request, response); request.setAttribute(MVC_RESULT_ATTRIBUTE, mvcResult); diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/RequestBuilder.java b/spring-test/src/main/java/org/springframework/test/web/servlet/RequestBuilder.java index f82e6aae027..9754a96c6b5 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/RequestBuilder.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/RequestBuilder.java @@ -1,3 +1,18 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.springframework.test.web.servlet; import javax.servlet.ServletContext; diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/SmartRequestBuilder.java b/spring-test/src/main/java/org/springframework/test/web/servlet/SmartRequestBuilder.java new file mode 100644 index 00000000000..6835caab4a0 --- /dev/null +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/SmartRequestBuilder.java @@ -0,0 +1,40 @@ +/* + * Copyright 2002-2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.test.web.servlet; + +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.test.web.servlet.request.RequestPostProcessor; + +/** + * Extended variant of a {@link RequestBuilder} that applies its + * {@link org.springframework.test.web.servlet.request.RequestPostProcessor}s + * as a separate step from the {@link #buildRequest} method. + * + * @author Rossen Stoyanchev + * @since 4.1 + */ +public interface SmartRequestBuilder extends RequestBuilder { + + /** + * Apply request post processing. Typically that means invoking one or more + * {@link org.springframework.test.web.servlet.request.RequestPostProcessor}s. + * + * @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/spring-test/src/main/java/org/springframework/test/web/servlet/request/ConfigurableSmartRequestBuilder.java b/spring-test/src/main/java/org/springframework/test/web/servlet/request/ConfigurableSmartRequestBuilder.java new file mode 100644 index 00000000000..cbb78c47239 --- /dev/null +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/request/ConfigurableSmartRequestBuilder.java @@ -0,0 +1,36 @@ +/* + * Copyright 2002-2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.test.web.servlet.request; + +import org.springframework.test.web.servlet.SmartRequestBuilder; + + +/** + * An extension of {@link org.springframework.test.web.servlet.SmartRequestBuilder + * SmartRequestBuilder} that can be configured with {@link RequestPostProcessor}s. + * + * @author Rossen Stoyanchev + * @since 4.1 + */ +public interface ConfigurableSmartRequestBuilder> + extends SmartRequestBuilder { + + /** + * Add the given {@code RequestPostProcessor}. + */ + B with(RequestPostProcessor requestPostProcessor); + +} diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilder.java b/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilder.java index 051d1ad0dd4..dafb0913178 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilder.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilder.java @@ -39,7 +39,6 @@ import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockHttpSession; import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.RequestBuilder; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.util.Assert; import org.springframework.util.LinkedMultiValueMap; @@ -68,7 +67,9 @@ import org.springframework.web.util.UriUtils; * @author Arjen Poutsma * @since 3.2 */ -public class MockHttpServletRequestBuilder implements RequestBuilder, Mergeable { +public class MockHttpServletRequestBuilder + implements ConfigurableSmartRequestBuilder, Mergeable { + private final HttpMethod method; @@ -421,6 +422,7 @@ public class MockHttpServletRequestBuilder implements RequestBuilder, Mergeable * and be made accessible through static factory methods. * @param postProcessor a post-processor to add */ + @Override public MockHttpServletRequestBuilder with(RequestPostProcessor postProcessor) { Assert.notNull(postProcessor, "postProcessor is required"); this.postProcessors.add(postProcessor); @@ -621,14 +623,6 @@ public class MockHttpServletRequestBuilder implements RequestBuilder, Mergeable 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); - if (request == null) { - throw new IllegalStateException("Post-processor [" + postProcessor.getClass().getName() + "] returned null"); - } - } - request.setAsyncSupported(true); return request; @@ -675,6 +669,18 @@ public class MockHttpServletRequestBuilder implements RequestBuilder, Mergeable return (flashMapManager != null ? flashMapManager : new SessionFlashMapManager()); } + @Override + public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) { + for (RequestPostProcessor postProcessor : this.postProcessors) { + request = postProcessor.postProcessRequest(request); + if (request == null) { + throw new IllegalStateException( + "Post-processor [" + postProcessor.getClass().getName() + "] returned null"); + } + } + return request; + } + private static void addToMultiValueMap(MultiValueMap map, String name, T[] values) { Assert.hasLength(name, "'name' must not be empty"); Assert.notNull(values, "'values' is required"); diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/setup/AbstractMockMvcBuilder.java b/spring-test/src/main/java/org/springframework/test/web/servlet/setup/AbstractMockMvcBuilder.java index f1bde2359cb..3bf75e7b732 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/setup/AbstractMockMvcBuilder.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/setup/AbstractMockMvcBuilder.java @@ -17,7 +17,14 @@ package org.springframework.test.web.servlet.setup; import org.springframework.mock.web.MockServletConfig; -import org.springframework.test.web.servlet.*; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MockMvcBuilderSupport; +import org.springframework.test.web.servlet.RequestBuilder; +import org.springframework.test.web.servlet.ResultHandler; +import org.springframework.test.web.servlet.ResultMatcher; +import org.springframework.test.web.servlet.request.ConfigurableSmartRequestBuilder; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.request.RequestPostProcessor; import org.springframework.util.Assert; import org.springframework.web.context.WebApplicationContext; @@ -38,7 +45,7 @@ import java.util.List; * @since 4.0 */ public abstract class AbstractMockMvcBuilder> - extends MockMvcBuilderSupport implements MockMvcBuilder { + extends MockMvcBuilderSupport implements ConfigurableMockMvcBuilder { private List filters = new ArrayList(); @@ -53,27 +60,6 @@ public abstract class AbstractMockMvcBuilder private final List configurers = new ArrayList(4); - - /** - * 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"); @@ -85,27 +71,6 @@ public abstract class AbstractMockMvcBuilder 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 - */ @SuppressWarnings("unchecked") public final T addFilter(Filter filter, String... urlPatterns) { @@ -120,70 +85,32 @@ public abstract class AbstractMockMvcBuilder 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.servlet.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.servlet.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.servlet.result.MockMvcResultHandlers} - */ @SuppressWarnings("unchecked") public final T alwaysDo(ResultHandler resultHandler) { this.globalResultHandlers.add(resultHandler); return (T) this; } - /** - * Whether to enable the DispatcherServlet property - * {@link org.springframework.web.servlet.DispatcherServlet#setDispatchOptionsRequest - * dispatchOptionsRequest} which allows processing of HTTP OPTIONS requests. - */ @SuppressWarnings("unchecked") public final T dispatchOptions(boolean dispatchOptions) { this.dispatchOptions = dispatchOptions; return (T) this; } - /** - * Add a {@code MockMvcConfigurer} which encapsulates ways to further configure - * this MockMvcBuilder with some specific purpose in mind. - */ @SuppressWarnings("unchecked") - public final T add(MockMvcConfigurer configurer) { + public final T apply(MockMvcConfigurer configurer) { configurer.afterConfigurerAdded(this); this.configurers.add(configurer); return (T) this; @@ -202,7 +129,15 @@ public abstract class AbstractMockMvcBuilder MockServletConfig mockServletConfig = new MockServletConfig(servletContext); for (MockMvcConfigurer configurer : this.configurers) { - configurer.beforeMockMvcCreated(this, this.defaultRequestBuilder, wac); + RequestPostProcessor processor = configurer.beforeMockMvcCreated(this, wac); + if (processor != null) { + if (this.defaultRequestBuilder == null) { + this.defaultRequestBuilder = MockMvcRequestBuilders.get("/"); + } + if (this.defaultRequestBuilder instanceof ConfigurableSmartRequestBuilder) { + ((ConfigurableSmartRequestBuilder) this.defaultRequestBuilder).with(processor); + } + } } Filter[] filterArray = this.filters.toArray(new Filter[this.filters.size()]); @@ -218,4 +153,4 @@ public abstract class AbstractMockMvcBuilder */ protected abstract WebApplicationContext initWebAppContext(); -} +} \ No newline at end of file diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/setup/ConfigurableMockMvcBuilder.java b/spring-test/src/main/java/org/springframework/test/web/servlet/setup/ConfigurableMockMvcBuilder.java new file mode 100644 index 00000000000..b44bd8cef95 --- /dev/null +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/setup/ConfigurableMockMvcBuilder.java @@ -0,0 +1,127 @@ +/* + * Copyright 2002-2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.test.web.servlet.setup; + +import org.springframework.test.web.servlet.MockMvcBuilder; +import org.springframework.test.web.servlet.RequestBuilder; +import org.springframework.test.web.servlet.ResultHandler; +import org.springframework.test.web.servlet.ResultMatcher; + +import javax.servlet.Filter; + +/** + * Defines common methods for building a {@code MockMvc}. + * + * @author Rossen Stoyanchev + * @since 4.1 + */ +public interface ConfigurableMockMvcBuilder> extends MockMvcBuilder { + + /** + * 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 + */ + T addFilters(Filter... filters); + + /** + * 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 + */ + T addFilter(Filter filter, String... urlPatterns); + + /** + * 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.servlet.request.MockMvcRequestBuilders} + * . + */ + T defaultRequest(RequestBuilder requestBuilder); + + /** + * 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.servlet.result.MockMvcResultMatchers} + */ + T alwaysExpect(ResultMatcher resultMatcher); + + /** + * 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.servlet.result.MockMvcResultHandlers} + */ + T alwaysDo(ResultHandler resultHandler); + + /** + * Whether to enable the DispatcherServlet property + * {@link org.springframework.web.servlet.DispatcherServlet#setDispatchOptionsRequest + * dispatchOptionsRequest} which allows processing of HTTP OPTIONS requests. + */ + T dispatchOptions(boolean dispatchOptions); + + /** + * Add a {@code MockMvcConfigurer} that automates MockMvc setup and + * configures it for some specific purpose (e.g. security). + */ + T apply(MockMvcConfigurer configurer); + +} diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/setup/MockMvcConfigurer.java b/spring-test/src/main/java/org/springframework/test/web/servlet/setup/MockMvcConfigurer.java index 0ee733762e7..36394103431 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/setup/MockMvcConfigurer.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/setup/MockMvcConfigurer.java @@ -5,7 +5,7 @@ * 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 + * 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, @@ -13,45 +13,42 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.test.web.servlet.setup; -import org.springframework.test.web.servlet.MockMvcBuilder; -import org.springframework.test.web.servlet.RequestBuilder; +import org.springframework.test.web.servlet.request.RequestPostProcessor; import org.springframework.web.context.WebApplicationContext; + /** - * A contract that allows the encapsulation of a "recipe" for configuring a - * MockMvcBuilder for some specific purpose. For example a 3rd party library - * may use this to provide convenient, easy ways to set up MockMvc. + * Allows a sub-class to encapsulate logic for pre-configuring a + * {@code ConfigurableMockMvcBuilder} for some specific purpose. A 3rd party + * library may use this to provide shortcuts for setting up MockMvc. * - *

Supported via {@link AbstractMockMvcBuilder#add(MockMvcConfigurer)} - * with instances of class likely created via static methods, e.g.: + *

Can be plugged in via {@link ConfigurableMockMvcBuilder#apply} with + * instances of this type likely created via static methods, e.g.: * *

- * 	MockMvcBuilders.webAppContextSetup(context)
- * 		.add(myLibrary("foo","bar").myProperty("foo"))
- * 		.build();
+ * 	MockMvcBuilders.webAppContextSetup(context).apply(mySetup("foo","bar")).build();
  * 
* * @author Rossen Stoyanchev * @since 4.1 + * @see org.springframework.test.web.servlet.setup.MockMvcConfigurerAdapter */ public interface MockMvcConfigurer { - /** - * Invoked immediately after a {@code MockMvcConfigurer} is configured via - * {@link AbstractMockMvcBuilder#add(MockMvcConfigurer)}. + * Invoked immediately after a {@code MockMvcConfigurer} is added via + * {@link ConfigurableMockMvcBuilder#apply}. */ - void afterConfigurerAdded(MockMvcBuilder mockMvcBuilder); + void afterConfigurerAdded(ConfigurableMockMvcBuilder builder); /** - * Invoked just before the MockMvc instance is built providing access to the - * configured "default" RequestBuilder. If a "default" RequestBuilder is - * needed but was not configured and is {@code null}), it can still be added - * via {@link AbstractMockMvcBuilder#defaultRequest}. + * Invoked just before the MockMvc instance is created. Implementations may + * return a RequestPostProcessor to be applied to every request performed + * through the created {@code MockMvc} instance. */ - void beforeMockMvcCreated(MockMvcBuilder mockMvcBuilder, RequestBuilder defaultRequestBuilder, - WebApplicationContext applicationContext); + RequestPostProcessor beforeMockMvcCreated(ConfigurableMockMvcBuilder builder, WebApplicationContext context); } diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/setup/MockMvcConfigurerAdapter.java b/spring-test/src/main/java/org/springframework/test/web/servlet/setup/MockMvcConfigurerAdapter.java new file mode 100644 index 00000000000..3afe4384021 --- /dev/null +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/setup/MockMvcConfigurerAdapter.java @@ -0,0 +1,41 @@ +/* + * Copyright 2002-2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.test.web.servlet.setup; + +import org.springframework.test.web.servlet.RequestBuilder; +import org.springframework.test.web.servlet.request.RequestPostProcessor; +import org.springframework.web.context.WebApplicationContext; + + +/** + * An empty method implementation of {@link MockMvcConfigurer}. + * + * @author Rossen Stoyanchev + * @since 4.1 + */ +public abstract class MockMvcConfigurerAdapter implements MockMvcConfigurer { + + @Override + public void afterConfigurerAdded(ConfigurableMockMvcBuilder builder) { + } + + @Override + public RequestPostProcessor beforeMockMvcCreated(ConfigurableMockMvcBuilder builder, WebApplicationContext cxt) { + return null; + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/RequestBuilderTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/FrameworkExtensionTests.java similarity index 56% rename from spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/RequestBuilderTests.java rename to spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/FrameworkExtensionTests.java index b36043a7e43..5ea48089acf 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/RequestBuilderTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/FrameworkExtensionTests.java @@ -15,6 +15,7 @@ */ package org.springframework.test.web.servlet.samples.standalone; +import static org.mockito.Mockito.mock; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -28,56 +29,72 @@ import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.stereotype.Controller; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.RequestPostProcessor; +import org.springframework.test.web.servlet.setup.ConfigurableMockMvcBuilder; +import org.springframework.test.web.servlet.setup.MockMvcConfigurerAdapter; +import org.springframework.util.Assert; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.context.WebApplicationContext; + +import java.security.Principal; /** - * Demonstrates how to implement and plug in a custom {@link RequestPostProcessor}. + * Demonstrates use of SPI extension points: + *
    + *
  • {@link org.springframework.test.web.servlet.request.RequestPostProcessor} + * for extending request building with custom methods. + *
  • {@link org.springframework.test.web.servlet.setup.MockMvcConfigurer + * MockMvcConfigurer} for extending MockMvc building with some automatic setup. + *
* * @author Rossen Stoyanchev */ -public class RequestBuilderTests { +public class FrameworkExtensionTests { private MockMvc mockMvc; + @Before public void setup() { - this.mockMvc = standaloneSetup(new SampleController()) - .defaultRequest(get("/").accept(MediaType.TEXT_PLAIN)) - .alwaysExpect(status().isOk()).build(); + this.mockMvc = standaloneSetup(new SampleController()).apply(defaultSetup()).build(); } + @Test public void fooHeader() throws Exception { - this.mockMvc.perform(get("/").with(headers().foo("a=b"))).andExpect( - content().string("Foo")); + this.mockMvc.perform(get("/").with(headers().foo("a=b"))).andExpect(content().string("Foo")); } @Test public void barHeader() throws Exception { - this.mockMvc.perform(get("/").with(headers().bar("a=b"))).andExpect( - content().string("Bar")); + this.mockMvc.perform(get("/").with(headers().bar("a=b"))).andExpect(content().string("Bar")); } - private static HeaderRequestPostProcessor headers() { - return new HeaderRequestPostProcessor(); + + private static TestMockMvcConfigurer defaultSetup() { + return new TestMockMvcConfigurer(); } + private static TestRequestPostProcessor headers() { + return new TestRequestPostProcessor(); + } + + /** - * Implementation of {@code RequestPostProcessor} with additional request - * building methods. + * Test {@code RequestPostProcessor}. */ - private static class HeaderRequestPostProcessor implements RequestPostProcessor { + private static class TestRequestPostProcessor implements RequestPostProcessor { private HttpHeaders headers = new HttpHeaders(); - public HeaderRequestPostProcessor foo(String value) { + + public TestRequestPostProcessor foo(String value) { this.headers.add("Foo", value); return this; } - public HeaderRequestPostProcessor bar(String value) { + public TestRequestPostProcessor bar(String value) { this.headers.add("Bar", value); return this; } @@ -91,19 +108,40 @@ public class RequestBuilderTests { } } + /** + * Test {@code MockMvcConfigurer}. + */ + private static class TestMockMvcConfigurer extends MockMvcConfigurerAdapter { + + @Override + public void afterConfigurerAdded(ConfigurableMockMvcBuilder builder) { + builder.alwaysExpect(status().isOk()); + } + + @Override + public RequestPostProcessor beforeMockMvcCreated(ConfigurableMockMvcBuilder builder, WebApplicationContext context) { + return request -> { + request.setUserPrincipal(mock(Principal.class)); + return request; + }; + } + } + @Controller @RequestMapping("/") private static class SampleController { @RequestMapping(headers = "Foo") @ResponseBody - public String handleFoo() { + public String handleFoo(Principal principal) { + Assert.isTrue(principal != null); return "Foo"; } @RequestMapping(headers = "Bar") @ResponseBody - public String handleBar() { + public String handleBar(Principal principal) { + Assert.isTrue(principal != null); return "Bar"; } }