Das Problem
Mit dem Rules Konstrukt verfügt JUnit seit einer ganzen Weile über die Möglichkeit, den Testablauf zu beeinflussen, also z.B. vor oder nach einem Test etwas zu tun.Das klingt erstmal absolut unspannend, immerhin sind Methoden, die mit @Before und @After Annotationen versehen werden nicht gänzlich unbekannt. Also was soll das Ganze?
Versuchen wir es mal mit einem einfachen Beispiel:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class SimpleTest { | |
@Before | |
public void before(){ | |
System.out.println("vor dem Test..."); | |
} | |
@After | |
public void after(){ | |
System.out.println("nach dem Test..."); | |
} | |
@Test | |
public void someTestCode(){ | |
System.out.println("test"); | |
} | |
} |
vor dem Test...
test
nach dem Test...
Nehmen wir an, uns gefällt was in den before() und after() Methoden passiert und wir möchten das wiederverwenden. Nichts einfacher als das:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class ExtendedTest extends SimpleTest { | |
@Test | |
public void moreTestCode(){ | |
System.out.println("extended test"); | |
} | |
} |
vor dem Test...
extended test
nach dem Test...
vor dem Test...
test
nach dem Test...
Durch Vererbung von JUnit Tests erhalten wir ein Verhalten, was nicht in jedem Fall gewünscht ist. Zunächst einmal werden alles Tests der Oberklasse ebenfalls ausgeführt. Des Weiteren haben wir keine Möglichkeit z.B. nur die before() Logik zu nutzen, ohne ebenfalls die Logik innerhalb der after() Methode auszuführen. Das Schränkt die Wiederverwendbarkeit stark ein und führt zu unübersichtlichen Klassenhierarchien.
Die Lösung: JUnit Rules
Mit Hilfe von JUnit Rules lässt sich das Problem elegent lösen. Die Rules stellen einen mit Interceptoren oder Aspekten vergleichbaren Mechanismus bereit unabhängig von der Klassenhierarchie zusätzliche Logik in den Testablauf einzubauen.Wie funktioniert es?
Um eine Rule zu definieren muss eine Klasse erstellt werden, die das Interface org.junit.rules.TestRule implementiert. Darin ist nur die Methode apply(Statement base, Description description) definiert. Hinter dem Statement-Objekt verbirgt sich der laufende JUnit Test welcher mit base.evaluate() ausgeführt werden kann. Davor und danach kann eigene Logik ausgeführt werden. Dies könnte z.B. wie folgt aussehen:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class MyRule implements TestRule { | |
@Override | |
public Statement apply(final Statement base, Description description) { | |
return new Statement() { | |
@Override | |
public void evaluate() throws Throwable { | |
System.out.println("vor dem Test..."); | |
base.evaluate(); | |
System.out.println("nach dem Test..."); | |
} | |
}; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class SimpleTest { | |
@Rule | |
public MyRule methodeRule = new MyRule(); | |
@Test | |
public void someTestCode(){ | |
System.out.println("test"); | |
} | |
} |
Das Ergebnis des Tests sieht wie folgt aus:
vor dem Test...
test
nach dem Test...
Die Rule wird beim Aufruf jeder einzelner Test-Methode verwendet, analog zu @Before bzw. @After. Alternativ lässt sich die Rule einmalig für die Testklasse verwenden, hierfür ist nur die folgende Änderung notwendig:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class SimpleTest { | |
@ClassRule | |
public static MyRule methodeRule = new MyRule(); | |
@Test | |
public void someTestCode(){ | |
System.out.println("test"); | |
} | |
} |
JUnit bringt bereits einige vorgefertigte Rules mit, siehe https://github.com/junit-team/junit/wiki/Rules