Skip to content

Servlet mocks are not reinjected into test instance between TestNG test methods [SPR-11626] #16249

Closed
@spring-projects-issues

Description

@spring-projects-issues

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

  1. Ensure that Servlet mocks managed by the ServletTestExecutionListener are re-injected into test instances between TestNG methods, if the RequestContextHolder is reset.

Affects: 3.2 GA

Reference URL: http://stackoverflow.com/questions/22712325/multiple-tests-with-autowired-mockhttpservletrequest-not-working

Issue Links:

Backported to: 3.2.9

Metadata

Metadata

Assignees

Labels

in: testIssues in the test modulestatus: backportedAn issue that has been backported to maintenance branchestype: bugA general bug

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions