-
Couldn't load subscription status.
- Fork 38.8k
Description
Sam Brannen opened SPR-11626 and commented
Status Quo
When executing TestNG-based integration tests that subclass AbstractTestNGSpringContextTests and are annotated with @WebAppConfiguration, if the ServletTestExecutionListener resets the request attributes stored in the RequestContextHolder after a test method, then any injected Servlet API mocks that are managed by the listener (e.g., MockHttpServletRequest, MockHttpServletResponse, and ServletWebRequest) will continue to hold values from the first such test method.
The net effect is that subsequent web tests will not have access to the current mocks.
See the referenced discussion on Stack Overflow for an example of a failing test case.
Analysis
The reason for this behavior is that ServletTestExecutionListener resets the request attributes after each test method, but DependencyInjectionTestExecutionListener does not re-inject dependencies before each test method (at least not by default). When a second test method is executed, an injected ServletRequest field will still reference the MockHttpServletRequest that was created for the previous test method; whereas, ServletTestExecutionListener creates a new instance of MockHttpServletRequest for each test method and sets it in the request attributes. Thus, the injected request and the one stored in the RequestContextHolder are only the same for the first test method that executes in TestNG.
Note: this bug only applies to TestNG tests; JUnit-based tests are not affected by this.
Temporary Work-Arounds
If you need a work-around before this fix is available, you have two options.
You can annotate the affected test methods with @DirtiesContext (or annotate your test class with @DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)). This will allow your tests to pass as you expect. The use of @DirtiesContext will make Spring close your test ApplicationContext after each test method, and this will likely have a negative impact on the speed of your tests; however, as of Spring 3.2.8 and 4.0.3, this is the only non-custom solution.
The second option is a more efficient work-around. Just define this custom TestExecutionListener in your project:
public class AlwaysReinjectDependenciesTestExecutionListener extends AbstractTestExecutionListener {
public void afterTestMethod(TestContext testContext) throws Exception {
testContext.setAttribute(
DependencyInjectionTestExecutionListener.REINJECT_DEPENDENCIES_ATTRIBUTE,
Boolean.TRUE);
}
}
And then annotate your test class like this:
@TestExecutionListeners(AlwaysReinjectDependenciesTestExecutionListener.class)
That should clear up any issues and keep your test suite running quickly.
Deliverables
- Ensure that Servlet mocks managed by the
ServletTestExecutionListenerare re-injected into test instances between TestNG methods, if theRequestContextHolderis reset.
Affects: 3.2 GA
Reference URL: http://stackoverflow.com/questions/22712325/multiple-tests-with-autowired-mockhttpservletrequest-not-working
Issue Links:
- ServletTestExecutionListener breaks old code [SPR-11144] #15771 ServletTestExecutionListener breaks old code
Backported to: 3.2.9