Domanda

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.

È stato utile?

Soluzione

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() { /* ... */ }

}

Altri suggerimenti

From the official docs, you can do something like this:

    @Rule
    public RuleChain chain= RuleChain
                           .outerRule(new LoggingRule("outer rule")
                           .around(new LoggingRule("middle rule")
                           .around(new LoggingRule("inner rule");

which logs:

 starting outer rule
 starting middle rule
 starting inner rule
 finished inner rule
 finished middle rule
 finished outer rule

Since junit 4.13 you can use the element order.

public class ThreeRules {
     @Rule(order = 0)
     public LoggingRule outer = new LoggingRule("outer rule");

     @Rule(order = 1)
     public LoggingRule middle = new LoggingRule("middle rule");

     @Rule(order = 2)
     public LoggingRule inner = new LoggingRule("inner rule");

     // ...
 }
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top