From 30291a8cd7b2808a7146b88593e2d514dea2138e Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Fri, 28 Oct 2016 14:31:44 +0200 Subject: [PATCH] Allow to customize TestDispatcherServlet This commit introduces the `DispatcherServletCustomizer` callback interface that can be used to customize the `DispatcherServlet` that a `MockMvc` is using. Previously, only the `dispatchOptions` flag can be customized. This commit allows to customize any property of `DispatcherServlet` before it is initialized. Issue: SPR-14277 --- .../servlet/DispatcherServletCustomizer.java | 37 +++++++++++++++++++ .../web/servlet/MockMvcBuilderSupport.java | 11 ++++-- .../servlet/setup/AbstractMockMvcBuilder.java | 15 ++++++-- .../setup/DefaultMockMvcBuilderTests.java | 32 ++++++++++++++++ 4 files changed, 88 insertions(+), 7 deletions(-) create mode 100644 spring-test/src/main/java/org/springframework/test/web/servlet/DispatcherServletCustomizer.java diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/DispatcherServletCustomizer.java b/spring-test/src/main/java/org/springframework/test/web/servlet/DispatcherServletCustomizer.java new file mode 100644 index 00000000000..14ecb8d3ad2 --- /dev/null +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/DispatcherServletCustomizer.java @@ -0,0 +1,37 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.test.web.servlet; + +import org.springframework.web.servlet.DispatcherServlet; + +/** + * Strategy interface for customizing {@link DispatcherServlet} instances that are + * managed by {@link MockMvc}. + * + * @author Stephane Nicoll + * @since 4.3.4 + */ +public interface DispatcherServletCustomizer { + + /** + * Customize the supplied {@link DispatcherServlet} before it is + * initialized. + * @param dispatcherServlet the dispatcher servlet to customize + */ + void customize(DispatcherServlet dispatcherServlet); + +} diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/MockMvcBuilderSupport.java b/spring-test/src/main/java/org/springframework/test/web/servlet/MockMvcBuilderSupport.java index 046597a518e..db7c4a2984d 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/MockMvcBuilderSupport.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/MockMvcBuilderSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,6 +35,7 @@ import org.springframework.web.context.WebApplicationContext; * * @author Rossen Stoyanchev * @author Rob Winch + * @author Stephane Nicoll * @since 3.2 */ public abstract class MockMvcBuilderSupport { @@ -42,12 +43,16 @@ public abstract class MockMvcBuilderSupport { protected final MockMvc createMockMvc(Filter[] filters, MockServletConfig servletConfig, WebApplicationContext webAppContext, RequestBuilder defaultRequestBuilder, List globalResultMatchers, List globalResultHandlers, - Boolean dispatchOptions) { + List dispatcherServletCustomizers) { ServletContext servletContext = webAppContext.getServletContext(); TestDispatcherServlet dispatcherServlet = new TestDispatcherServlet(webAppContext); - dispatcherServlet.setDispatchOptionsRequest(dispatchOptions); + if (dispatcherServletCustomizers != null) { + for (DispatcherServletCustomizer customizers : dispatcherServletCustomizers) { + customizers.customize(dispatcherServlet); + } + } try { dispatcherServlet.init(servletConfig); } 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 98227c80f65..620c565ca9f 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 @@ -22,6 +22,7 @@ import javax.servlet.Filter; import javax.servlet.ServletContext; import org.springframework.mock.web.MockServletConfig; +import org.springframework.test.web.servlet.DispatcherServletCustomizer; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MockMvcBuilderSupport; import org.springframework.test.web.servlet.RequestBuilder; @@ -42,6 +43,7 @@ import org.springframework.web.context.WebApplicationContext; * pass to the DispatcherServlet. * * @author Rossen Stoyanchev + * @author Stephane Nicoll * @since 4.0 */ public abstract class AbstractMockMvcBuilder> @@ -55,7 +57,7 @@ public abstract class AbstractMockMvcBuilder private final List globalResultHandlers = new ArrayList<>(); - private Boolean dispatchOptions = Boolean.TRUE; + private final List dispatcherServletCustomizers = new ArrayList<>(); private final List configurers = new ArrayList<>(4); @@ -104,11 +106,16 @@ public abstract class AbstractMockMvcBuilder } @SuppressWarnings("unchecked") - public final T dispatchOptions(boolean dispatchOptions) { - this.dispatchOptions = dispatchOptions; + public final T addDispatcherServletCustomizer(DispatcherServletCustomizer customizer) { + this.dispatcherServletCustomizers.add(customizer); return (T) this; } + public final T dispatchOptions(boolean dispatchOptions) { + return addDispatcherServletCustomizer( + dispatcherServlet -> dispatcherServlet.setDispatchOptionsRequest(dispatchOptions)); + } + @SuppressWarnings("unchecked") public final T apply(MockMvcConfigurer configurer) { configurer.afterConfigurerAdded(this); @@ -144,7 +151,7 @@ public abstract class AbstractMockMvcBuilder Filter[] filterArray = this.filters.toArray(new Filter[this.filters.size()]); return super.createMockMvc(filterArray, mockServletConfig, wac, this.defaultRequestBuilder, - this.globalResultMatchers, this.globalResultHandlers, this.dispatchOptions); + this.globalResultMatchers, this.globalResultHandlers, this.dispatcherServletCustomizers); } /** diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/setup/DefaultMockMvcBuilderTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/setup/DefaultMockMvcBuilderTests.java index d30ad4180ae..d566fb54a69 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/setup/DefaultMockMvcBuilderTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/setup/DefaultMockMvcBuilderTests.java @@ -20,11 +20,14 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.springframework.beans.DirectFieldAccessor; import org.springframework.context.support.StaticApplicationContext; import org.springframework.mock.web.MockServletContext; +import org.springframework.test.web.servlet.MockMvc; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.StaticWebApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; +import org.springframework.web.servlet.DispatcherServlet; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; @@ -36,6 +39,7 @@ import static org.springframework.test.web.servlet.setup.MockMvcBuilders.*; * @author Rob Winch * @author Sebastien Deleuze * @author Sam Brannen + * @author Stephane Nicoll */ public class DefaultMockMvcBuilderTests { @@ -124,4 +128,32 @@ public class DefaultMockMvcBuilderTests { assertSame(root, WebApplicationContextUtils.getRequiredWebApplicationContext(this.servletContext)); } + /** + * See /SPR-14277 + */ + @Test + public void dispatcherServletCustomizer() { + StubWebApplicationContext root = new StubWebApplicationContext(this.servletContext); + DefaultMockMvcBuilder builder = webAppContextSetup(root); + builder.addDispatcherServletCustomizer(ds -> ds.setContextId("test-id")); + builder.dispatchOptions(true); + MockMvc mvc = builder.build(); + DispatcherServlet ds = (DispatcherServlet) new DirectFieldAccessor(mvc) + .getPropertyValue("servlet"); + assertEquals("test-id", ds.getContextId()); + } + + @Test + public void dispatcherServletCustomizerProcessedInOrder() { + StubWebApplicationContext root = new StubWebApplicationContext(this.servletContext); + DefaultMockMvcBuilder builder = webAppContextSetup(root); + builder.addDispatcherServletCustomizer(ds -> ds.setContextId("test-id")); + builder.addDispatcherServletCustomizer(ds -> ds.setContextId("override-id")); + builder.dispatchOptions(true); + MockMvc mvc = builder.build(); + DispatcherServlet ds = (DispatcherServlet) new DirectFieldAccessor(mvc) + .getPropertyValue("servlet"); + assertEquals("override-id", ds.getContextId()); + } + }