JUnit Rules are a handy solution if one needs to alter test methods or wants to share common functionality between several test cases. JUnit 4.10 introduced a new class to order several rules according to our needs using a so called rule-chain.
In the following example, we’re going to create a simple custom rule and afterwards bind several instances of it in a specified order to a test method.
Adding JUnit
Just one Maven dependency needed here – JUnit 4.10
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
Rule definition
Now we need to define a role – as you might remember a role consists of an annotation (that’s what is used in the test case) and and implementation class that implements TestRule.
We’re going a simple rule that adds some console logging to our test methods – it takes a note as constructor string argument and prints it when the rule is applied later.
package com.hascode.tutorial;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
public class ConsoleOutRule implements TestRule {
private final String note;
public ConsoleOutRule(final String note) {
this.note = note;
}
public Statement apply(final Statement base, final Description description) {
System.out.println("rule applied. note: " + note);
return base;
}
}
Applying the Rule to a Test Case
If we wanted to modify a test with our new rule, this could be a scenario
package com.hascode.tutorial;
import static org.junit.Assert.assertTrue;
import org.junit.Rule;
import org.junit.Test;
public class SomeUnitTest {
@Rule
public ConsoleOutRule rule = new ConsoleOutRule("somewhere");
@Test
public void testSomeMethod() {
System.out.println("test started");
assertTrue(true);
}
}
Running the test would produce the following output:
rule applied. note: somewhere
test started
Ordering Rules with RuleChains
Finally we’re wrapping several rules using RuleChain’s static builder methods
package com.hascode.tutorial;
import static org.junit.Assert.assertTrue;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.RuleChain;
import org.junit.rules.TestRule;
public class SomeUnitTestWithRuleChain {
@Rule
public TestRule chain = RuleChain.outerRule(new ConsoleOutRule("outer"))
.around(new ConsoleOutRule("middle"))
.around(new ConsoleOutRule("inner"));
@Test
public void testSomeMethod() {
System.out.println("test started");
assertTrue(true);
}
}
The following output is produced
rule applied. note: inner
rule applied. note: middle
rule applied. note: outer
test started
Ordering ClassRules with RuleChains
The following example show how to order class rules:
package com.hascode.tutorial;
import static org.junit.Assert.assertTrue;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.rules.ExternalResource;
import org.junit.rules.RuleChain;
import org.junit.rules.TestRule;
public class SomeUnitTestWithRuleChain {
@ClassRule
public static TestRule chain = RuleChain.outerRule(new ConsoleOutTestWrapper("outer"))
.around(new ConsoleOutTestWrapper("middle"))
.around(new ConsoleOutTestWrapper("inner"));
@Test
public void testSomeMethod1() {
System.out.println("test 1 started");
assertTrue(true);
}
@Test
public void testSomeMethod2() {
System.out.println("test 2 started");
assertTrue(true);
}
static class ConsoleOutTestWrapper extends ExternalResource {
private final String note;
ConsoleOutTestWrapper(String note) {
this.note = note;
}
@Override
protected void before() throws Throwable {
System.out.printf("[BEFORE] rule applied. note: %s%n", note);
}
@Override
protected void after() {
System.out.printf("[AFTER] rule applied. note: %s%n", note);
}
}
}
This produces the following output:
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running com.hascode.tutorial.SomeUnitTestWithRuleChain
[BEFORE] rule applied. note: outer
[BEFORE] rule applied. note: middle
[BEFORE] rule applied. note: inner
test2 started
test1 started
[AFTER] rule applied. note: inner
[AFTER] rule applied. note: middle
[AFTER] rule applied. note: outer
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.029 sec
Tutorial Sources
I have put the source from this tutorial on my GitHub repository – download it there or check it out:
git clone https://github.com/hascode/junit-rule-chaining-samples
Article Updates
-
2018-11-28: Typo in ConsoleOutTestWrapper fixed (thanks @Serg for mentioning)
-
2018-06-24: Examples for ordering class rules added.