提交 71b63cd9 编写于 作者: R Rossen Stoyanchev

Update MockMvcConfigurer support

This is a follow-up on the commit introducing MockMvcConfigurer:
https://github.com/spring-projects/spring-framework/commit/c2b0fac852dd9f865699fb374ad78543767fec05

This commit refines the MockMvcConfigurer contract to use (the new)
ConfigurableMockMvcBuilder hence not requiring downcasting to
AbstractMockMvcBuilder.

The same also no longer passes the "default" RequestBuilder which would
also require a downcast, but rather allows a RequestPostProcessor to be
returned so that a 3rd party framework or application can modify any
property of every performed MockHttpServletRequest.

To make this possible the new SmartRequestBuilder interface separates
request building from request post processing while the new
ConfigurableSmartRequestBuilder allows adding a RequestPostProcessor
to a MockMvcBuilder.

Issue: SPR-11497
上级 988499f7
...@@ -134,6 +134,10 @@ public final class MockMvc { ...@@ -134,6 +134,10 @@ public final class MockMvc {
MockHttpServletRequest request = requestBuilder.buildRequest(this.servletContext); MockHttpServletRequest request = requestBuilder.buildRequest(this.servletContext);
MockHttpServletResponse response = new MockHttpServletResponse(); MockHttpServletResponse response = new MockHttpServletResponse();
if (requestBuilder instanceof SmartRequestBuilder) {
request = ((SmartRequestBuilder) requestBuilder).postProcessRequest(request);
}
final MvcResult mvcResult = new DefaultMvcResult(request, response); final MvcResult mvcResult = new DefaultMvcResult(request, response);
request.setAttribute(MVC_RESULT_ATTRIBUTE, mvcResult); request.setAttribute(MVC_RESULT_ATTRIBUTE, mvcResult);
......
/*
* 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; package org.springframework.test.web.servlet;
import javax.servlet.ServletContext; import javax.servlet.ServletContext;
......
/*
* 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);
}
/*
* 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<B extends ConfigurableSmartRequestBuilder<B>>
extends SmartRequestBuilder {
/**
* Add the given {@code RequestPostProcessor}.
*/
B with(RequestPostProcessor requestPostProcessor);
}
...@@ -39,7 +39,6 @@ import org.springframework.mock.web.MockHttpServletRequest; ...@@ -39,7 +39,6 @@ import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockHttpSession; import org.springframework.mock.web.MockHttpSession;
import org.springframework.test.web.servlet.MockMvc; 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.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.LinkedMultiValueMap;
...@@ -68,7 +67,9 @@ import org.springframework.web.util.UriUtils; ...@@ -68,7 +67,9 @@ import org.springframework.web.util.UriUtils;
* @author Arjen Poutsma * @author Arjen Poutsma
* @since 3.2 * @since 3.2
*/ */
public class MockHttpServletRequestBuilder implements RequestBuilder, Mergeable { public class MockHttpServletRequestBuilder
implements ConfigurableSmartRequestBuilder<MockHttpServletRequestBuilder>, Mergeable {
private final HttpMethod method; private final HttpMethod method;
...@@ -421,6 +422,7 @@ public class MockHttpServletRequestBuilder implements RequestBuilder, Mergeable ...@@ -421,6 +422,7 @@ public class MockHttpServletRequestBuilder implements RequestBuilder, Mergeable
* and be made accessible through static factory methods. * and be made accessible through static factory methods.
* @param postProcessor a post-processor to add * @param postProcessor a post-processor to add
*/ */
@Override
public MockHttpServletRequestBuilder with(RequestPostProcessor postProcessor) { public MockHttpServletRequestBuilder with(RequestPostProcessor postProcessor) {
Assert.notNull(postProcessor, "postProcessor is required"); Assert.notNull(postProcessor, "postProcessor is required");
this.postProcessors.add(postProcessor); this.postProcessors.add(postProcessor);
...@@ -621,14 +623,6 @@ public class MockHttpServletRequestBuilder implements RequestBuilder, Mergeable ...@@ -621,14 +623,6 @@ public class MockHttpServletRequestBuilder implements RequestBuilder, Mergeable
FlashMapManager flashMapManager = getFlashMapManager(request); FlashMapManager flashMapManager = getFlashMapManager(request);
flashMapManager.saveOutputFlashMap(flashMap, request, new MockHttpServletResponse()); 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); request.setAsyncSupported(true);
return request; return request;
...@@ -675,6 +669,18 @@ public class MockHttpServletRequestBuilder implements RequestBuilder, Mergeable ...@@ -675,6 +669,18 @@ public class MockHttpServletRequestBuilder implements RequestBuilder, Mergeable
return (flashMapManager != null ? flashMapManager : new SessionFlashMapManager()); 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 <T> void addToMultiValueMap(MultiValueMap<String, T> map, String name, T[] values) { private static <T> void addToMultiValueMap(MultiValueMap<String, T> map, String name, T[] values) {
Assert.hasLength(name, "'name' must not be empty"); Assert.hasLength(name, "'name' must not be empty");
Assert.notNull(values, "'values' is required"); Assert.notNull(values, "'values' is required");
......
...@@ -17,7 +17,14 @@ ...@@ -17,7 +17,14 @@
package org.springframework.test.web.servlet.setup; package org.springframework.test.web.servlet.setup;
import org.springframework.mock.web.MockServletConfig; 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.util.Assert;
import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.WebApplicationContext;
...@@ -38,7 +45,7 @@ import java.util.List; ...@@ -38,7 +45,7 @@ import java.util.List;
* @since 4.0 * @since 4.0
*/ */
public abstract class AbstractMockMvcBuilder<B extends AbstractMockMvcBuilder<B>> public abstract class AbstractMockMvcBuilder<B extends AbstractMockMvcBuilder<B>>
extends MockMvcBuilderSupport implements MockMvcBuilder { extends MockMvcBuilderSupport implements ConfigurableMockMvcBuilder<B> {
private List<Filter> filters = new ArrayList<Filter>(); private List<Filter> filters = new ArrayList<Filter>();
...@@ -53,27 +60,6 @@ public abstract class AbstractMockMvcBuilder<B extends AbstractMockMvcBuilder<B> ...@@ -53,27 +60,6 @@ public abstract class AbstractMockMvcBuilder<B extends AbstractMockMvcBuilder<B>
private final List<MockMvcConfigurer> configurers = new ArrayList<MockMvcConfigurer>(4); private final List<MockMvcConfigurer> configurers = new ArrayList<MockMvcConfigurer>(4);
/**
* Add filters mapped to any request (i.e. "/*"). For example:
*
* <pre class="code">
* mockMvcBuilder.addFilters(springSecurityFilterChain);
* </pre>
*
* <p>is the equivalent of the following web.xml configuration:
*
* <pre class="code">
* &lt;filter-mapping&gt;
* &lt;filter-name&gt;springSecurityFilterChain&lt;/filter-name&gt;
* &lt;url-pattern&gt;/*&lt;/url-pattern&gt;
* &lt;/filter-mapping&gt;
* </pre>
*
* <p>Filters will be invoked in the order in which they are provided.
*
* @param filters the filters to add
*/
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public final <T extends B> T addFilters(Filter... filters) { public final <T extends B> T addFilters(Filter... filters) {
Assert.notNull(filters, "filters cannot be null"); Assert.notNull(filters, "filters cannot be null");
...@@ -85,27 +71,6 @@ public abstract class AbstractMockMvcBuilder<B extends AbstractMockMvcBuilder<B> ...@@ -85,27 +71,6 @@ public abstract class AbstractMockMvcBuilder<B extends AbstractMockMvcBuilder<B>
return (T) this; return (T) this;
} }
/**
* Add a filter mapped to a specific set of patterns. For example:
*
* <pre class="code">
* mockMvcBuilder.addFilters(myResourceFilter, "/resources/*");
* </pre>
*
* <p>is the equivalent of:
*
* <pre class="code">
* &lt;filter-mapping&gt;
* &lt;filter-name&gt;myResourceFilter&lt;/filter-name&gt;
* &lt;url-pattern&gt;/resources/*&lt;/url-pattern&gt;
* &lt;/filter-mapping&gt;
* </pre>
*
* <p>Filters will be invoked in the order in which they are provided.
*
* @param filter the filter to add
* @param urlPatterns URL patterns to map to; if empty, "/*" is used by default
*/
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public final <T extends B> T addFilter(Filter filter, String... urlPatterns) { public final <T extends B> T addFilter(Filter filter, String... urlPatterns) {
...@@ -120,70 +85,32 @@ public abstract class AbstractMockMvcBuilder<B extends AbstractMockMvcBuilder<B> ...@@ -120,70 +85,32 @@ public abstract class AbstractMockMvcBuilder<B extends AbstractMockMvcBuilder<B>
return (T) this; return (T) this;
} }
/**
* Define default request properties that should be merged into all
* performed requests. In effect this provides a mechanism for defining
* common initialization for all requests such as the content type, request
* parameters, session attributes, and any other request property.
*
* <p>Properties specified at the time of performing a request override the
* default properties defined here.
*
* @param requestBuilder a RequestBuilder; see static factory methods in
* {@link org.springframework.test.web.servlet.request.MockMvcRequestBuilders}
* .
*/
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public final <T extends B> T defaultRequest(RequestBuilder requestBuilder) { public final <T extends B> T defaultRequest(RequestBuilder requestBuilder) {
this.defaultRequestBuilder = requestBuilder; this.defaultRequestBuilder = requestBuilder;
return (T) this; return (T) this;
} }
/**
* Define a global expectation that should <em>always</em> be applied to
* every response. For example, status code 200 (OK), content type
* {@code "application/json"}, etc.
*
* @param resultMatcher a ResultMatcher; see static factory methods in
* {@link org.springframework.test.web.servlet.result.MockMvcResultMatchers}
*/
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public final <T extends B> T alwaysExpect(ResultMatcher resultMatcher) { public final <T extends B> T alwaysExpect(ResultMatcher resultMatcher) {
this.globalResultMatchers.add(resultMatcher); this.globalResultMatchers.add(resultMatcher);
return (T) this; return (T) this;
} }
/**
* Define a global action that should <em>always</em> be applied to every
* response. For example, writing detailed information about the performed
* request and resulting response to {@code System.out}.
*
* @param resultHandler a ResultHandler; see static factory methods in
* {@link org.springframework.test.web.servlet.result.MockMvcResultHandlers}
*/
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public final <T extends B> T alwaysDo(ResultHandler resultHandler) { public final <T extends B> T alwaysDo(ResultHandler resultHandler) {
this.globalResultHandlers.add(resultHandler); this.globalResultHandlers.add(resultHandler);
return (T) this; 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") @SuppressWarnings("unchecked")
public final <T extends B> T dispatchOptions(boolean dispatchOptions) { public final <T extends B> T dispatchOptions(boolean dispatchOptions) {
this.dispatchOptions = dispatchOptions; this.dispatchOptions = dispatchOptions;
return (T) this; return (T) this;
} }
/**
* Add a {@code MockMvcConfigurer} which encapsulates ways to further configure
* this MockMvcBuilder with some specific purpose in mind.
*/
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public final <T extends B> T add(MockMvcConfigurer configurer) { public final <T extends B> T apply(MockMvcConfigurer configurer) {
configurer.afterConfigurerAdded(this); configurer.afterConfigurerAdded(this);
this.configurers.add(configurer); this.configurers.add(configurer);
return (T) this; return (T) this;
...@@ -202,7 +129,15 @@ public abstract class AbstractMockMvcBuilder<B extends AbstractMockMvcBuilder<B> ...@@ -202,7 +129,15 @@ public abstract class AbstractMockMvcBuilder<B extends AbstractMockMvcBuilder<B>
MockServletConfig mockServletConfig = new MockServletConfig(servletContext); MockServletConfig mockServletConfig = new MockServletConfig(servletContext);
for (MockMvcConfigurer configurer : this.configurers) { 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()]); Filter[] filterArray = this.filters.toArray(new Filter[this.filters.size()]);
...@@ -218,4 +153,4 @@ public abstract class AbstractMockMvcBuilder<B extends AbstractMockMvcBuilder<B> ...@@ -218,4 +153,4 @@ public abstract class AbstractMockMvcBuilder<B extends AbstractMockMvcBuilder<B>
*/ */
protected abstract WebApplicationContext initWebAppContext(); protected abstract WebApplicationContext initWebAppContext();
} }
\ No newline at end of file
/*
* 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<B extends ConfigurableMockMvcBuilder<B>> extends MockMvcBuilder {
/**
* Add filters mapped to any request (i.e. "/*"). For example:
*
* <pre class="code">
* mockMvcBuilder.addFilters(springSecurityFilterChain);
* </pre>
*
* <p>is the equivalent of the following web.xml configuration:
*
* <pre class="code">
* &lt;filter-mapping&gt;
* &lt;filter-name&gt;springSecurityFilterChain&lt;/filter-name&gt;
* &lt;url-pattern&gt;/*&lt;/url-pattern&gt;
* &lt;/filter-mapping&gt;
* </pre>
*
* <p>Filters will be invoked in the order in which they are provided.
*
* @param filters the filters to add
*/
<T extends B> T addFilters(Filter... filters);
/**
* Add a filter mapped to a specific set of patterns. For example:
*
* <pre class="code">
* mockMvcBuilder.addFilters(myResourceFilter, "/resources/*");
* </pre>
*
* <p>is the equivalent of:
*
* <pre class="code">
* &lt;filter-mapping&gt;
* &lt;filter-name&gt;myResourceFilter&lt;/filter-name&gt;
* &lt;url-pattern&gt;/resources/*&lt;/url-pattern&gt;
* &lt;/filter-mapping&gt;
* </pre>
*
* <p>Filters will be invoked in the order in which they are provided.
*
* @param filter the filter to add
* @param urlPatterns URL patterns to map to; if empty, "/*" is used by default
*/
<T extends B> 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.
*
* <p>Properties specified at the time of performing a request override the
* default properties defined here.
*
* @param requestBuilder a RequestBuilder; see static factory methods in
* {@link org.springframework.test.web.servlet.request.MockMvcRequestBuilders}
* .
*/
<T extends B> T defaultRequest(RequestBuilder requestBuilder);
/**
* Define a global expectation that should <em>always</em> be applied to
* every response. For example, status code 200 (OK), content type
* {@code "application/json"}, etc.
*
* @param resultMatcher a ResultMatcher; see static factory methods in
* {@link org.springframework.test.web.servlet.result.MockMvcResultMatchers}
*/
<T extends B> T alwaysExpect(ResultMatcher resultMatcher);
/**
* Define a global action that should <em>always</em> be applied to every
* response. For example, writing detailed information about the performed
* request and resulting response to {@code System.out}.
*
* @param resultHandler a ResultHandler; see static factory methods in
* {@link org.springframework.test.web.servlet.result.MockMvcResultHandlers}
*/
<T extends B> 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 extends B> T dispatchOptions(boolean dispatchOptions);
/**
* Add a {@code MockMvcConfigurer} that automates MockMvc setup and
* configures it for some specific purpose (e.g. security).
*/
<T extends B> T apply(MockMvcConfigurer configurer);
}
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * 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 * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
...@@ -13,45 +13,42 @@ ...@@ -13,45 +13,42 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.test.web.servlet.setup; package org.springframework.test.web.servlet.setup;
import org.springframework.test.web.servlet.MockMvcBuilder; import org.springframework.test.web.servlet.request.RequestPostProcessor;
import org.springframework.test.web.servlet.RequestBuilder;
import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.WebApplicationContext;
/** /**
* A contract that allows the encapsulation of a "recipe" for configuring a * Allows a sub-class to encapsulate logic for pre-configuring a
* MockMvcBuilder for some specific purpose. For example a 3rd party library * {@code ConfigurableMockMvcBuilder} for some specific purpose. A 3rd party
* may use this to provide convenient, easy ways to set up MockMvc. * library may use this to provide shortcuts for setting up MockMvc.
* *
* <p>Supported via {@link AbstractMockMvcBuilder#add(MockMvcConfigurer)} * <p>Can be plugged in via {@link ConfigurableMockMvcBuilder#apply} with
* with instances of class likely created via static methods, e.g.: * instances of this type likely created via static methods, e.g.:
* *
* <pre class="code"> * <pre class="code">
* MockMvcBuilders.webAppContextSetup(context) * MockMvcBuilders.webAppContextSetup(context).apply(mySetup("foo","bar")).build();
* .add(myLibrary("foo","bar").myProperty("foo"))
* .build();
* </pre> * </pre>
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @since 4.1 * @since 4.1
* @see org.springframework.test.web.servlet.setup.MockMvcConfigurerAdapter
*/ */
public interface MockMvcConfigurer { public interface MockMvcConfigurer {
/** /**
* Invoked immediately after a {@code MockMvcConfigurer} is configured via * Invoked immediately after a {@code MockMvcConfigurer} is added via
* {@link AbstractMockMvcBuilder#add(MockMvcConfigurer)}. * {@link ConfigurableMockMvcBuilder#apply}.
*/ */
void afterConfigurerAdded(MockMvcBuilder mockMvcBuilder); void afterConfigurerAdded(ConfigurableMockMvcBuilder<?> builder);
/** /**
* Invoked just before the MockMvc instance is built providing access to the * Invoked just before the MockMvc instance is created. Implementations may
* configured "default" RequestBuilder. If a "default" RequestBuilder is * return a RequestPostProcessor to be applied to every request performed
* needed but was not configured and is {@code null}), it can still be added * through the created {@code MockMvc} instance.
* via {@link AbstractMockMvcBuilder#defaultRequest}.
*/ */
void beforeMockMvcCreated(MockMvcBuilder mockMvcBuilder, RequestBuilder defaultRequestBuilder, RequestPostProcessor beforeMockMvcCreated(ConfigurableMockMvcBuilder<?> builder, WebApplicationContext context);
WebApplicationContext applicationContext);
} }
/*
* 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;
}
}
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
*/ */
package org.springframework.test.web.servlet.samples.standalone; 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.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
...@@ -28,56 +29,72 @@ import org.springframework.mock.web.MockHttpServletRequest; ...@@ -28,56 +29,72 @@ import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.RequestPostProcessor; 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.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody; 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:
* <ul>
* <li> {@link org.springframework.test.web.servlet.request.RequestPostProcessor}
* for extending request building with custom methods.
* <li> {@link org.springframework.test.web.servlet.setup.MockMvcConfigurer
* MockMvcConfigurer} for extending MockMvc building with some automatic setup.
* </ul>
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
*/ */
public class RequestBuilderTests { public class FrameworkExtensionTests {
private MockMvc mockMvc; private MockMvc mockMvc;
@Before @Before
public void setup() { public void setup() {
this.mockMvc = standaloneSetup(new SampleController()) this.mockMvc = standaloneSetup(new SampleController()).apply(defaultSetup()).build();
.defaultRequest(get("/").accept(MediaType.TEXT_PLAIN))
.alwaysExpect(status().isOk()).build();
} }
@Test @Test
public void fooHeader() throws Exception { public void fooHeader() throws Exception {
this.mockMvc.perform(get("/").with(headers().foo("a=b"))).andExpect( this.mockMvc.perform(get("/").with(headers().foo("a=b"))).andExpect(content().string("Foo"));
content().string("Foo"));
} }
@Test @Test
public void barHeader() throws Exception { public void barHeader() throws Exception {
this.mockMvc.perform(get("/").with(headers().bar("a=b"))).andExpect( this.mockMvc.perform(get("/").with(headers().bar("a=b"))).andExpect(content().string("Bar"));
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 * Test {@code RequestPostProcessor}.
* building methods.
*/ */
private static class HeaderRequestPostProcessor implements RequestPostProcessor { private static class TestRequestPostProcessor implements RequestPostProcessor {
private HttpHeaders headers = new HttpHeaders(); private HttpHeaders headers = new HttpHeaders();
public HeaderRequestPostProcessor foo(String value) {
public TestRequestPostProcessor foo(String value) {
this.headers.add("Foo", value); this.headers.add("Foo", value);
return this; return this;
} }
public HeaderRequestPostProcessor bar(String value) { public TestRequestPostProcessor bar(String value) {
this.headers.add("Bar", value); this.headers.add("Bar", value);
return this; return this;
} }
...@@ -91,19 +108,40 @@ public class RequestBuilderTests { ...@@ -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 @Controller
@RequestMapping("/") @RequestMapping("/")
private static class SampleController { private static class SampleController {
@RequestMapping(headers = "Foo") @RequestMapping(headers = "Foo")
@ResponseBody @ResponseBody
public String handleFoo() { public String handleFoo(Principal principal) {
Assert.isTrue(principal != null);
return "Foo"; return "Foo";
} }
@RequestMapping(headers = "Bar") @RequestMapping(headers = "Bar")
@ResponseBody @ResponseBody
public String handleBar() { public String handleBar(Principal principal) {
Assert.isTrue(principal != null);
return "Bar"; return "Bar";
} }
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册