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
ServletTestExecutionListener
are re-injected into test instances between TestNG methods, if theRequestContextHolder
is 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