-
Notifications
You must be signed in to change notification settings - Fork 12
Deep dive into rules order with RuleSequence
RuleSequence
is a replacement of standart JUnit4 RuleChain class.
There are 3 lists of rules:
- firstRules - will be executed firstly
- rules - will be executed normally after firstRules
- lastRules - will be executed last
The rules provided in constructor will be added to normal rules.
An order of rules execution depends on 2 things:
- the priority of rules list
- the order these rules were added to
RuleSequence
.
RuleSequence(normalRule)
.addLast(rule1, rule2)
.add(rule3, rule4)
.addFirst(rule5, rule6)
In case above rules will be executed in following order: rule5, rule6, normalRule, rule3, rule4, rule1, rule2
I didn't find this info in any junit doc or anywhere else. But it's really important to understand.
Lets look at some example of how standart junit rules invoke their methods. We take for example TestWatcher
subclass.
class MyTestWatcherRule(val name: String): TestWatcher() {
override fun starting(description: Description?) {
Log.debug("$name starting")
}
override fun finished(description: Description?) {
Log.debug("$name finished")
}
}
Let's make a little experiment with junit RuleChain
.
class TestWatcherSampleTest {
@get:Rule
val ruleChain = RuleChain
.outerRule(MyTestWatcherRule("rule 1"))
.around(MyTestWatcherRule("rule 2"))
@Test
fun someTest(){
Log.debug("test")
}
}
We execute test and open log.
rule 1 starting
rule 2 starting
test
rule 2 finished
rule 1 finished
As you can see finished
method is execudet in reverse order. We don't wanna explain why it works so strange. It actually can blow our mind. (We realise that it's some kind of junit concept but it's really inconvenient to use)
To tell you the truth RuleSequence
works almost the same way. But almost is really important word here.
If we just replace RuleChain
to RuleSequence
the order of methods will be the same. And it even broke the logic of internal lists priority for such methods. But we found the solution in RuleSequenceTearDown
interface.
Let's mark that our custom class implement this interface.
class MyTestWatcherRule(val name: String): TestWatcher(), RuleSequenceTearDown {
//there is no change
}
Replace RuleChain
to RuleSequence
class TestWatcherSampleTest {
@get:Rule
val ruleChain = RuleSequence()
.add(MyTestWatcherRule("rule 1"))
.add(MyTestWatcherRule("rule 2"))
@Test
fun someTest(){
Log.debug("test")
}
}
And finally the order becomes proper.
rule 1 starting
rule 2 starting
test
rule 1 finished
rule 2 finished
As you might have guessed the TearDownRule
also implements RuleSequenceTearDown
interface and works in the same way.