Ordering your JUnit Rules using a RuleChain
February 21st, 2012 by Micha KopsJUnit 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.
Contents
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 test 2 started test 1 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 Bitbucket repository – download it there or check it out using Mercurial:
hg clone https://bitbucket.org/hascode/junit-rule-chaining-samples
Resources
Article Updates
- 2018-11-28: Typo in ConsoleOutTestWrapper fixed (thanks @Serg for mentioning)
- 2018-06-24: Examples for ordering class rules added.
Tags: Api, classrule, externalresource, junit, rule, rulechain, tdd, testing, testrule
February 23rd, 2012 at 6:41 am
Where did you see that a rule needs a corresponding annotation? Removing @ConsoleOut bares the same result.
February 23rd, 2012 at 8:04 pm
geesh thanks for mentioning .. these were fragments of a first approach where I would have read data from the annotation used in the rule implementation. i’ve updated the article! damn nightly builds :)
March 8th, 2018 at 5:47 pm
the console output shows inner, middle, outer, but as far as I can tell from the api documentation it should show outer, middle, inner.
June 19th, 2018 at 8:54 pm
Can you RuleChain with ClassRules?
June 24th, 2018 at 5:33 pm
Calfilmmaker: I’ve added an example using ClassRules!
November 26th, 2018 at 11:37 pm
Slight typo — the after() method inside the ConsoleOutTestWrapper class should probably be changed to do
System.out.printf(“[AFTER] rule applied. note: %s%n”, note);
instead of “[BEFORE]…”
November 28th, 2018 at 9:58 pm
Hi Serg,
thanks! I had fixed the typo only in the sources. I’ve updated the article.
Cheers,
Micha