From cd9d7cfe4fea81ac886ebd2e477f2d90841aa2ac Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Sat, 29 Mar 2014 21:14:49 +0100 Subject: [PATCH] Reinject Servlet mocks between TestNG test methods Prior to this commit, if multiple test methods were executed in a subclass of AbstractTestNGSpringContextTests annotated with @WebAppConfiguration, then injected Servlet API mocks would only reference the mocks created for the first test method. Subsequent test methods could therefore never reference the current mocks, and there was a discrepancy between the state of the injected mocks and the mock set in the RequestContextHolder. This commit addresses this issue by ensuring that dependencies (including updated mocks) are injected into the test instance before the next test method if the ServletTestExecutionListener resets the request attributes in RequestContextHolder. Issue: SPR-11626 (cherry picked from commit c38600762dd17c5b4f1dabeb42c2dff77b5a66d1) --- .../web/ServletTestExecutionListener.java | 16 ++-- ...ecutionListenerTestNGIntegrationTests.java | 78 +++++++++++++++++++ ...xecutionListenerJUnitIntegrationTests.java | 78 +++++++++++++++++++ 3 files changed, 167 insertions(+), 5 deletions(-) create mode 100644 spring-test/src/test/java/org/springframework/test/context/testng/web/ServletTestExecutionListenerTestNGIntegrationTests.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/web/ServletTestExecutionListenerJUnitIntegrationTests.java diff --git a/spring-test/src/main/java/org/springframework/test/context/web/ServletTestExecutionListener.java b/spring-test/src/main/java/org/springframework/test/context/web/ServletTestExecutionListener.java index 8f3fc809f8f..a5574d9fe38 100644 --- a/spring-test/src/main/java/org/springframework/test/context/web/ServletTestExecutionListener.java +++ b/spring-test/src/main/java/org/springframework/test/context/web/ServletTestExecutionListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * 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. @@ -31,6 +31,7 @@ import org.springframework.mock.web.MockServletContext; import org.springframework.test.context.TestContext; import org.springframework.test.context.TestExecutionListener; import org.springframework.test.context.support.AbstractTestExecutionListener; +import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; import org.springframework.util.Assert; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.request.RequestContextHolder; @@ -115,11 +116,14 @@ public class ServletTestExecutionListener extends AbstractTestExecutionListener } /** - * Cleans up thread-local state after each test method by {@linkplain + * If the {@link #RESET_REQUEST_CONTEXT_HOLDER_ATTRIBUTE} in the supplied + * {@code TestContext} has a value of {@link Boolean#TRUE}, this method will + * (1) clean up thread-local state after each test method by {@linkplain * RequestContextHolder#resetRequestAttributes() resetting} Spring Web's - * {@code RequestContextHolder}, but only if the {@link - * #RESET_REQUEST_CONTEXT_HOLDER_ATTRIBUTE} in the supplied {@code TestContext} - * has a value of {@link Boolean#TRUE}. + * {@code RequestContextHolder} and (2) ensure that new mocks are injected + * into the test instance for subsequent tests by setting the + * {@link DependencyInjectionTestExecutionListener#REINJECT_DEPENDENCIES_ATTRIBUTE} + * in the test context to {@code true}. * *

The {@link #RESET_REQUEST_CONTEXT_HOLDER_ATTRIBUTE} and * {@link #POPULATED_REQUEST_CONTEXT_HOLDER_ATTRIBUTE} will be subsequently @@ -134,6 +138,8 @@ public class ServletTestExecutionListener extends AbstractTestExecutionListener logger.debug(String.format("Resetting RequestContextHolder for test context %s.", testContext)); } RequestContextHolder.resetRequestAttributes(); + testContext.setAttribute(DependencyInjectionTestExecutionListener.REINJECT_DEPENDENCIES_ATTRIBUTE, + Boolean.TRUE); } testContext.removeAttribute(POPULATED_REQUEST_CONTEXT_HOLDER_ATTRIBUTE); testContext.removeAttribute(RESET_REQUEST_CONTEXT_HOLDER_ATTRIBUTE); diff --git a/spring-test/src/test/java/org/springframework/test/context/testng/web/ServletTestExecutionListenerTestNGIntegrationTests.java b/spring-test/src/test/java/org/springframework/test/context/testng/web/ServletTestExecutionListenerTestNGIntegrationTests.java new file mode 100644 index 00000000000..55aa29e75ec --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/testng/web/ServletTestExecutionListenerTestNGIntegrationTests.java @@ -0,0 +1,78 @@ +/* + * 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.context.testng.web; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; +import org.springframework.test.context.web.ServletTestExecutionListener; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import org.testng.annotations.Test; + +import static org.junit.Assert.*; + +/** + * TestNG-based integration tests for {@link ServletTestExecutionListener}. + * + * @author Sam Brannen + * @since 3.2.9 + * @see org.springframework.test.context.web.ServletTestExecutionListenerJUnitIntegrationTests + */ +@ContextConfiguration +@WebAppConfiguration +public class ServletTestExecutionListenerTestNGIntegrationTests extends AbstractTestNGSpringContextTests { + + @Configuration + static class Config { + /* no beans required for this test */ + } + + + @Autowired + private MockHttpServletRequest servletRequest; + + + /** + * Verifies bug fix for SPR-11626. + * + * @see #ensureMocksAreReinjectedBetweenTests_2 + */ + @Test + public void ensureMocksAreReinjectedBetweenTests_1() { + assertInjectedServletRequestEqualsRequestInRequestContextHolder(); + } + + /** + * Verifies bug fix for SPR-11626. + * + * @see #ensureMocksAreReinjectedBetweenTests_1 + */ + @Test + public void ensureMocksAreReinjectedBetweenTests_2() { + assertInjectedServletRequestEqualsRequestInRequestContextHolder(); + } + + private void assertInjectedServletRequestEqualsRequestInRequestContextHolder() { + assertEquals("Injected ServletRequest must be stored in the RequestContextHolder", servletRequest, + ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest()); + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/web/ServletTestExecutionListenerJUnitIntegrationTests.java b/spring-test/src/test/java/org/springframework/test/context/web/ServletTestExecutionListenerJUnitIntegrationTests.java new file mode 100644 index 00000000000..f8dfa2c9558 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/web/ServletTestExecutionListenerJUnitIntegrationTests.java @@ -0,0 +1,78 @@ +/* + * 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.context.web; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import static org.junit.Assert.*; + +/** + * JUnit-based integration tests for {@link ServletTestExecutionListener}. + * + * @author Sam Brannen + * @since 3.2.9 + * @see org.springframework.test.context.testng.web.ServletTestExecutionListenerTestNGIntegrationTests + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +@WebAppConfiguration +public class ServletTestExecutionListenerJUnitIntegrationTests { + + @Configuration + static class Config { + /* no beans required for this test */ + } + + + @Autowired + private MockHttpServletRequest servletRequest; + + + /** + * Verifies bug fix for SPR-11626. + * + * @see #ensureMocksAreReinjectedBetweenTests_2 + */ + @Test + public void ensureMocksAreReinjectedBetweenTests_1() { + assertInjectedServletRequestEqualsRequestInRequestContextHolder(); + } + + /** + * Verifies bug fix for SPR-11626. + * + * @see #ensureMocksAreReinjectedBetweenTests_1 + */ + @Test + public void ensureMocksAreReinjectedBetweenTests_2() { + assertInjectedServletRequestEqualsRequestInRequestContextHolder(); + } + + private void assertInjectedServletRequestEqualsRequestInRequestContextHolder() { + assertEquals("Injected ServletRequest must be stored in the RequestContextHolder", servletRequest, + ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest()); + } + +}