We have a timeout enforced per-test via a test rule:
public abstract class BaseTestCase {
@Rule
public final Timeout timeout = new Timeout(60*1000);
}
Then our GUI tests obviously have to run on the EDT, so they have a test rule for that too:
public class TestSomeController extends BaseTestCase {
@Rule
public final RunOnEventDispatchThread runOnEventDispatchThread =
new RunOnEventDispatchThread();
@Test
public void testStuff() { /* ... */ }
}
Both of these rules involves messing with the thread the test runs on. Timeout
creates a new thread and then kills it N milliseconds later. RunOnEventDispatchThread
runs a Runnable
on the EDT and then waits for the task to complete.
Up to JUnit 4.10 this has been working fine, but we just upgraded to JUnit 4.11 and now it seems that the ordering of test rules has changed.
The current JUnit seems to be applying the rules in "logical" order, so it applies Timeout
and then RunOnEventDispatchThread
.
This means that RunOnEventDispatchThread
ends up on the "outside" of the Statement
"onion". So it runs the task on the EDT, but that task is now "spawn a new thread to run the test" - because it's spawning a new thread, the test fails, because Swing calls are now being called on the wrong thread.
Obviously JUnit 4.10 was running the test rules in another order. Is there a way to influence this?
Note that existing answers to this sort of question have all been addressed by using RuleChain
. As far as I can tell, we cannot use RuleChain
because that class demands both rules to be in the same class, and ours are not.
I am not very familiar with the rule ordering in JUnit and especially the RuleChain
class. So I had a quick look at this class' documentation. The example code snippet seems to be your solution when being used in a template method sense.
I could imagine that following mechanism will work:
public abstract class BaseTestCase {
@Rule
public final RuleChain ruleChain = createRuleChain();
private RuleChain createRuleChain() {
return appendInnerRules(RuleChain.outerRule(createOuterRule()));
}
protected TestRule createOuterRule() {
return new Timeout(60 * 1000);
}
protected RuleChain appendInnerRules(RuleChain ruleChain) {
return ruleChain; // default behavior
}
}
public final class TestSomeController extends BaseTestCase {
protected RuleChain appendInnerRules(RuleChain ruleChain) {
return ruleChain.around(new RunOnEventDispatchThread());
}
@Test
public void testStuff() { /* ... */ }
}