Donnerstag, 31. Oktober 2013

JUnit Rules

Vor einiger Zeit bin ich zufällig über eine weniger bekannte Funktionalität von JUnit gestoßen: JUnit Rules.

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:

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");
}
}
view raw SimpleTest.java hosted with ❤ by GitHub
Das Ergebnis davon sieht wenig überraschend wie folgt aus:

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:

public class ExtendedTest extends SimpleTest {
@Test
public void moreTestCode(){
System.out.println("extended test");
}
}
Das Ergebnis der Ausführung dieser Klasse mit Hilfe von JUnit sieht wie folgt aus:

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:

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...");
}
};
}
}
view raw MyRule.java hosted with ❤ by GitHub
Um die Rule im eigenen JUnit Test verwenden zu können wird diese einfach als Klassenvariable instanziiert und mit der @Rule Annotation versehen:
public class SimpleTest {
@Rule
public MyRule methodeRule = new MyRule();
@Test
public void someTestCode(){
System.out.println("test");
}
}
view raw SImpleTest.java hosted with ❤ by GitHub

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:
public class SimpleTest {
@ClassRule
public static MyRule methodeRule = new MyRule();
@Test
public void someTestCode(){
System.out.println("test");
}
}
view raw SimpleTest.java hosted with ❤ by GitHub

JUnit bringt bereits einige vorgefertigte Rules mit, siehe https://github.com/junit-team/junit/wiki/Rules

Keine Kommentare:

Kommentar veröffentlichen