Ordering your JUnit Rules using a RuleChain

February 21st, 2012 by

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
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: , , , , , , , ,

7 Responses to “Ordering your JUnit Rules using a RuleChain”

  1. John Says:

    Where did you see that a rule needs a corresponding annotation? Removing @ConsoleOut bares the same result.

  2. micha kops Says:

    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 :)

  3. ryan Says:

    the console output shows inner, middle, outer, but as far as I can tell from the api documentation it should show outer, middle, inner.

  4. Calfilmmaker Says:

    Can you RuleChain with ClassRules?

  5. Micha Kops Says:

    Calfilmmaker: I’ve added an example using ClassRules!

  6. Serg Says:

    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]…”

  7. Micha Kops Says:

    Hi Serg,

    thanks! I had fixed the typo only in the sources. I’ve updated the article.

    Cheers,

    Micha

Search
Categories