Combine jUnit timeout with @DirtiesContext

I have a testcase that modifies my spring context and might run into an infinite loop. After the testcase I’d like to have a clean spring context so I use the the @DirtiesContext annotation. In case of the infinite loop I added a jUnit timeout.

@Test(timeout = 1000)
@DirtiesContext
public void testTimeout() {
    //test stuff
}

The problem is that if the test runs into the timeout, jUnit seems to terminate it in a way that spring doesn’t handle the context reset anymore. Same behaviour if I add @DirtiesContext on class level.

Is there another way to achieve what I’m trying to do?

Answer

To configure a timeout with JUnit and the Spring TestContext Framework (TCF), you have two main categories of options:

  1. preemptive: If you use JUnit’s Timeout rule or the timeout attribute of @Test, your test and all nested rules, associated before/after methods, TCF callbacks, etc. will execute in a separate thread that is preemptively aborted if the timeout is exceeded.
  2. non-preemptive: If you use Spring’s @Timed annotation with the TCF, your test and all nested rules, associated before/after methods, TCF callbacks, etc. will execute in the same thread as the test runner, and the test will only fail if it terminates normally and the timeout has been exceeded.

Both the Timeout rule and the timeout attribute of @Test result in a FailOnTimeout statement being used under the hood; and FailOnTimeout executes the next statement in the chain within a separate thread which is preemptively terminated if the timeout is exceeded. Thus, the behavior for both of these JUnit-based configuration options would naturally be identical: execution of the statement chain ends prematurely without giving any after callbacks a chance to perform their tasks. The result is that the afterTestMethod() callback in Spring’s DirtiesContextTestExecutionListener is never invoked in such scenarios, and consequently the context would not be dirtied.

Is there another way to achieve what I’m trying to do?

For starters, using Spring’s @Timed annotation is not an option for you since it will not preemptively abort code that runs in an infinite loop.

You mentioned that you discovered a work-around by using the @DirtiesContext(methodMode = BEFORE_METHOD) feature introduced in Spring Framework 4.2. That may work in certain circumstances, but this work-around is fragile at best: using this work-around results in the context being closed and restarted before this particular method; however, if the code in question modifies the context before the preemptive timeout, your Spring context will remain corrupt for subsequent test methods.

The only real solution is to ensure that you use @DirtiesContext with the BEFORE_METHOD mode on the next method that executes after the method that was preemptively aborted due to a timeout. However… knowing exactly which method is the “next” method might be the challenging part… unless you use JUnit’s @FixMethodOrder support.

Regards,

Sam (author of the Spring TestContext Framework)

Leave a Reply

Your email address will not be published. Required fields are marked *