Question

I use an @Autowired MockHttpServletRequest in some of my Spring tests. TestNG is used as testing framework. If I only have one test method in the class this works fine. However, if there are multiple test methods, only the first run test uses my MockHttpServletRequest. Let me illustrate with an example:

@WebAppConfiguration
@ContextConfiguration({"classpath:applicationContext.xml"})
public class FooTest extends AbstractTestNGSpringContextTests {

    @Autowired
    private MockHttpServletRequest servletRequest;

    @Test
    public void test1() {
        assertEquals(((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest(), servletRequest);
    }

    @Test
    public void test2() {
        assertEquals(((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest(), servletRequest);
    }

}

In this example, test1() passes, but test2() fails! If you run the test methods individually, they both pass. Why does one test fail if they are run together?

I tried to dig in the code, there seems to be some kind of reset of the request attributes after a test method have run, but I didn't find a way to turn it off. My Spring version is 3.2.8.RELEASE.

Was it helpful?

Solution

UPDATE: This has been fixed in Spring Framework 3.2.9, 4.0.4, and 4.1. See SPR-11626 for details.


Well, my friend... you have discovered a bug in the Spring TestContext Framework.

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 (by default). When the second test method is executed, the servletRequest field is still referencing 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.

Since I am the author of this code, I have to personally apologize, but... I'll make sure it gets fixed ASAP. See SPR-11626 for details on the status of the fix. ;)

Note: this bug only applies to TestNG tests; this does not apply to JUnit tests.

As a work-around, 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.

Having said that, the following is a much 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.

Regards,

Sam

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top