JUnit Rules
Marc Philipp, andrena objects ag
Stefan Birkner, Immobilien Scout GmbH
Erschienen in Java aktuell, 1/2012, dem Magazin der iJUG.
Automatisierte Tests sind aus der heutigen Softwareentwicklung nicht mehr wegzudenken. JUnit ist das älteste und bekannteste Testing-Framework für Java. Doch selbst ein so etabliertes und einfach zu benutzendes Framework wird kontinuierlich weiterentwickelt. Eine der Neuerungen sind JUnit Rules, die Entwicklern eine neue mächtige Möglichkeit bieten, Tests zu formulieren und besser zu strukturieren.
Der Legende nach haben Kent Beck und Erich Gamma 1997 den Kern von JUnit auf dem Weg zu einer Konferenz im Flugzeug zwischen Zürich und Atlanta geschrieben. JUnit griff die Idee wieder auf, die Beck 1994 mit SUnit [1] für Smalltalk eingeführt hatte: ein Testing-Framework, dessen Zielgruppe Programmierer sind, also dieselben Leute, die auch den Code schreiben, den es zu testen gilt. JUnit ist inzwischen weit verbreitet. Es wird nicht nur zum Schreiben von Unittests, sondern auch zur Automatisierung von Integrations- und Akzeptanztests verwendet.
Viele erfolgreiche Open-Source-Projekte zeichnen sich dadurch aus, dass mit der Zeit immer neue Features eingebaut werden. Dies führt häufig dazu, dass einst simple Bibliotheken unübersichtlich und schwer wartbar werden. JUnit geht hier gezielt einen anderen Weg. David Saff, neben Kent Beck der zweite Maintainer von JUnit, sieht das so: „JUnit is the intersection of all possible useful Java test frameworks, not their union”.
Die Wahrnehmung in der Java-Entwicklergemeinde ist dementsprechend: Da JUnit so einfach ist, meint jeder, der es schon einmal benutzt hat, es gut zu kennen. Das ist einerseits gut, denn die Hürde Unittests zu schreiben ist so sehr niedrig. Andererseits führt es dazu, dass Neuerungen von vielen Entwicklern gar nicht oder erst verzögert wahrgenommen werden. Fragt man Entwicklerkollegen nach Neuerungen in JUnit, wird häufig die Umstellung von Vererbung auf Annotations-basierte Testschreibweise in Version 4.0 erwähnt.
Seitdem hat sich allerdings einiges getan. Die neueste Innovation, die mit Version 4.7 eingeführt wurde, heißt Rules. Zugegeben, unter dem Begriff kann man sich erst einmal nichts vorstellen. Hat man sich diese „Regeln” für Tests aber einmal eingehend angesehen — und genau das werden wir in diesem Artikel tun — stellt man fest: Rules werden die Art, wie wir JUnit-Tests schreiben, nachhaltig verändern.
Was sind Rules?
Mithilfe von JUnit-Rules lässt sich die Ausführung von Tests beeinflussen. Ähnlich einem Aspekt in der aspektorientierten Programmierung (AOP) kann die Rule Code vor, nach oder anstelle einer Testmethode ausführen [2]. Hinter dieser abstrakten Beschreibung steckt ein mächtiges Werkzeug, wie die folgenden Beispiele zeigen.
Standard-Rules
JUnit selbst liefert fünf Rules mit, an denen wir den praktischen Einsatz zeigen (der Quellcode aller Beispiele ist auf GitHub verfügbar [3]).
Temporäre Dateien
Beim Testen von Code, der Dateioperationen ausführt, steht man häufig vor dem Problem, dass der Test temporär eine Datei benötigt, die nach dem Test wieder gelöscht werden soll. Bisher brachte man den entsprechenden Code in @Before- und @After-Methoden unter, wie das folgende Beispiel zeigt.
public class TemporaryFolderWithoutRule {
private File folder;
@Before
public void createTemporaryFolder() throws Exception {
folder = File.createTempFile("myFolder", "");
folder.delete();
folder.mkdir();
}
@Test
public void test() throws Exception {
File file = new File(folder, "test.txt");
file.createNewFile();
assertTrue(file.exists());
}
@After
public void deleteTemporaryFolder() {
recursivelyDelete(folder);
}
private void recursivelyDelete(File file) {
File[] files = file.listFiles();
if (files != null) {
for (File each : files) {
recursivelyDelete(each);
}
}
file.delete();
}
}
Dieser Test kann mit der TemporaryFolder-Rule wesentlich kürzer und prägnanter formuliert werden, da die Rule den Framework-Code kapselt.
Um die Rule zu verwenden, muss innerhalb des Tests ein Feld vom Typ TemporaryFolder angelegt werden. Dieses Feld muss public sein und mit der Annotation @Rule markiert werden, sodass JUnit die Rule erkennt. So markierte Rules wirken sich auf die Ausführung aller Testmethoden einer Testklasse aus.
public class TemporaryFolderWithRule {
@Rule
public TemporaryFolder folder = new TemporaryFolder();
@Test
public void test() throws Exception {
File file = folder.newFile("test.txt");
assertTrue(file.exists());
}
}
Die Testmethode test() legt mithilfe der TemporaryFolder-Rule die Datei test.txt an und überprüft danach, dass die Datei erzeugt wurde. Doch wo wurde die Datei erzeugt? Der Name TemporaryFolder suggeriert es bereits: in einem temporären Ordner. Doch die Rule legt die Datei nicht nur an, sondern löscht sie nach dem Test auch wieder, inklusive des temporären Ordners.
Timeout
Es kommt gelegentlich vor, dass man Code schreibt, der versehentlich Endlosschleifen enthält. Ein JUnit-Test, der diese Codestellen testet, läuft in diese Endlosschleifen. Bei Verwendung der Timeout-Rule schlagen solche Tests fehl, da sie nicht innerhalb der vorgegebenen Zeit beendet werden.
public class GlobalTimeout {
@Rule
public Timeout timeout = new Timeout(20); //timeout nach 20 ms
@Test
public void firstTest() {
while (true) {}
}
@Test
public void secondTest() {
for (;;) {}
}
}
Führt man diesen Test aus, schlagen beide Testmethoden fehl. Würde man die Rule nicht verwenden, liefe dieser Test endlos.
Wer bisher den timeout-Parameter der @Test-Annotation verwendet hat, kann diesen durch die Timeout-Rule ersetzen. Die Rule bietet den Vorteil, dass sie nur einmal in der Klasse definiert werden muss und dann für alle Testmethoden gilt.
Erwartete Exceptions
Schon bisher kann das Auftreten von Exceptions mit dem expected-Parameter der @Test-Annotation getestet werden. Die ExpectedException-Rule erweitert die Test-Möglichkeiten für Exceptions. Damit lassen sich neben der Klasse auch die Message und mittels Hamcrest-Matchern sogar beliebige Details der geworfenen Exception testen.
public class ExpectedExceptionWithRule {
int[] threeNumbers = { 1, 2, 3 };
@Rule public ExpectedException thrown = ExpectedException.none();
@Test
public void exception() {
thrown.expect(ArrayIndexOutOfBoundsException.class);
threeNumbers[3] = 4;
}
@Test
public void exceptionWithMessage() {
thrown.expect(ArrayIndexOutOfBoundsException.class);
thrown.expectMessage("3");
threeNumbers[3] = 4;
}
}
Fehler sammeln
Üblicherweise bricht ein Test nach der ersten fehlgeschlagenen Assertion ab. Will man in einem Test trotzdem alle Assertions abarbeiten, kann man den ErrorCollector verwenden. Er sammelt fehlgeschlagene Assertions innerhalb einer Testmethode und gibt am Ende eine Liste der Fehlschläge aus. So kann man etwa alle Elemente in einer Liste überprüfen und den Test erst am Ende fehlschlagen lassen, wenn die Überprüfung eines oder mehrerer Elemente fehlgeschlagen ist.
public class ErrorCollectingTest {
@Rule
public ErrorCollector collector = new ErrorCollector();
@Test
public void test() {
collector.checkThat(1 + 1, is(3));
collector.addError(new Exception("something went wrong"));
}
}
Wenn man diesen Test ausführt, erhält man zwei Fehlernachrichten mit jeweils einem Stacktrace, der einen zu der Zeile im Programmcode führt, wo die Überprüfung fehlgeschlagen ist.
Testname
Um innerhalb einer Testmethode auf deren Namen zuzugreifen, kann man die TestName-Rule verwendet.
public class NameRuleTest {
@Rule
public TestName test = new TestName();
@Test
public void test() {
assertThat(test.getMethodName(), is("test"));
}
}
Rules selber schreiben
Die von JUnit bereitgestellten Rules sind nur der Anfang. Wer sich das Schreiben von Tests erleichtern will, kann seine eigenen Rules schreiben. Das sind letztendlich Klassen, die das Interface TestRule mit der Methode apply(...) implementieren. Für die häufigsten Anwendungsfälle greift uns JUnit unter die Arme und stellt die drei Templateklassen ExternalResource, TestWatcher und Verifier zur Verfügung.
Bereitstellung externer Ressourcen
Vielfach werden, insbesondere bei Integrationstests, externe Ressourcen wie Dateien, Server oder Verbindungen benötigt. Diese müssen dem Test zur Verfügung gestellt und nach dessen Ausführung wieder aufgeräumt werden.
Dieses Ressourcenhandling lässt sich recht einfach mit einer Rule abbilden, indem man von der Basisklasse ExternalResource ableitet. In der neuen Rule überschreibt man die before()-Methode, um die Ressource bereitzustellen, und die after()-Methode um sie nach dem Test wieder aufzuräumen. Ein Beispiel hierfür ist die TemporaryFolder-Rule, die in der before()-Methode ein neues Verzeichnis erstellt und es in der after()-Methode wieder löscht.
Wie einfach sich eine solche Rule schreiben lässt, demonstriert das folgende Beispiel. Möchte man für einen Test sicherstellen, dass eine System Property einen bestimmten Wert hat und nach dem Test der alte Wert wiederhergestellt wird, könnte man die Methoden before() und after() wie folgt implementieren:
public class ProvideSystemProperty extends ExternalResource {
private final String key, value;
private String oldValue;
public ProvideSystemProperty(String key, String value) {
this.key = key;
this.value = value;
}
@Override
protected void before() {
oldValue = System.getProperty(key);
System.setProperty(key, value);
}
@Override
protected void after() {
if (oldValue == null) {
System.clearProperty(key);
} else {
System.setProperty(key, oldValue);
}
}
}
Und schon kann man die Rule in einem Test verwenden:
public class SomeTestUsingSystemProperty {
@Rule
public ProvideSystemProperty property = new ProvideSystemProperty("someKey", "someValue");
@Test
public void test() {
assertThat(System.getProperty("someKey"), is("someValue"));
}
}
Benachrichtigung über die Testausführung
Da man mit einer Rule Code vor und nach dem Aufruf der Testmethoden ausführen kann, lässt sich damit eine Benachrichtigung über die Testausführung realisieren. Dazu stellt JUnit die abstrakte Oberklasse TestWatcher bereit. Diese besitzt vier leer implementierte Methoden, die man nach Bedarf überschreiben kann: starting(), succeeded(), failed() und finished():
public class BeepOnFailure extends TestWatcher {
@Override
protected void failed(Throwable e, Description description) {
Toolkit.getDefaultToolkit().beep();
}
}
Die Benutzung in einem Test sieht dann so aus:
public class FailingTestThatBeeps {
@Rule
public BeepOnFailure beep = new BeepOnFailure();
@Test
public void test() {
fail();
}
}
Überprüfungen nach den Tests
Das dritte von JUnit zur Verfügung gestellte Template ist der Verifier. Dort kann man die Methode verify() überschreiben, die nach jedem erfolgreichen Test ausgeführt wird. In dieser Methode lassen sich zusätzliche Überprüfungen unterbringen, die im Fehlerfall eine Exception werfen, um den Test doch noch scheitern zu lassen.
Eine Beispielimplementierung von Verifier ist der weiter oben vorgestellte ErrorCollector. Während des Testlaufs sammelt er alle fehlgeschlagenen Assertions und wirft im Fehlerfall eine MultipleFailureException am Ende des Tests.
TestRule implementieren
Anstatt eines der Templates zu verwenden kann man das Interface TestRule auch direkt implementieren. Dieses Interface hat genau eine Methode
Statement apply(Statement base, Description description);
Das erste Argument base kapselt den auszuführenden Test, der sich mittels evaluate() ausführen lässt. Die description stellt Informationen zum Test zu Verfügung (bspw. den Testnamen). Der Rückgabewert der Methode ist ein Statement dass anstelle des Tests ausgeführt wird. Üblicherweise delegiert das neue Statement den Aufruf von evaluate() an den ursprünglichen Test und führt zusätzlich weitere Methoden aus. Der folgende Code zeigt beispielhaft die leicht abgewandelte Implementierung des ExternalResource-Templates.
public Statement apply(Statement base, Description description) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
before();
try {
base.evaluate();
} finally {
after();
}
}
};
}
Hier wird zuerst die Template-Methode before() ausgeführt, dann der Test selbst mittels base.evaluate() und zum Schluss die zweite Template-Methode after().
Regeln auf Klassenebene
Alle Rules, die wir bisher gesehen haben, wurden für jede Methode einzeln angewandt, genauso wie Methoden, die mit @Before und @After annotiert sind, vor bzw. nach jedem Test ausgeführt werden. Manchmal möchte man allerdings die Möglichkeit haben, Code nur einmal vor der ersten bzw. nach der letzten Testmethode in einer Klasse auszuführen. Ein häufiger Anwendungsfall sind Integrationstests, die eine Verbindung zu einem Server aufbauen und wieder schließen müssen. Das war bisher nur mit den Annotations @BeforeClass bzw. @AfterClass möglich, Rules konnte man dazu nicht verwenden. Um dieses Problem zu lösen, wurde in JUnit 4.9 die @ClassRule-Annotation eingeführt.
Um eine ClassRule zu verwenden, annotiert man ein Feld in der Testklasse, das analog zu @BeforeClass–/@AfterClass-Methoden public und static sein muss. Der Typ des Feldes muss wie bei der @Rule-Annotation das TestRule-Interface implementieren. Eine solche Rule lässt sich nicht nur in einer normalen Testklasse verwenden, sondern auch in einer Test-Suite, wie das folgende Beispiel [4] illustriert:
@RunWith(Suite.class)
@SuiteClasses({A.class, B.class, C.class})
public class UsesExternalResource {
public static Server myServer = new Server();
@ClassRule
public static ExternalResource connection = new ExternalResource() {
@Override protected void before() throws Throwable {
myServer.connect();
};
@Override protected void after() {
myServer.disconnect();
};
};
}
Mehrere Regeln kombinieren
Einen weiteren Vorteil von Rules gegenüber Hilfsmethoden in Testoberklassen stellt ihre Kombinierbarkeit dar. Es lassen sich beliebig viele Rules in einem Test verwenden:
public class CombiningMultipleRules {
@Rule public TestRule beep = new BeepOnFailure();
@Rule public ExpectedException exceptions = ExpectedException.none();
@Rule public TestName test = new TestName();
@Test
public void test() {
exceptions.expect(IllegalArgumentException.class);
throw new RuntimeException("Hello from " + test.getMethodName());
}
}
Das funktioniert wunderbar, solange die Rules voneinander unabhängig sind. JUnit macht absichtlich keinerlei Zusicherungen was die Reihenfolge der Abarbeitung von Rules angeht [5]. Manchmal möchte man aber dennoch eine bestimmte Reihenfolge vorgeben. Angenommen man hat zwei Rules, von denen die erste eine bestimmte Ressource zur Verfügung stellt, die von der zweiten Rule benutzt wird. Dann möchte man sehr wohl sicherstellen, dass zuerst die Ressource bereitgestellt wird, bevor sie konsumiert wird. Dafür wurde in JUnit 4.10 die RuleChain-Klasse eingeführt. RuleChain implementiert selbst das TestRule-Interface, kann also verwendet werden, wie eine normale Rule [6]:
public class UseRuleChain {
@Rule
public TestRule chain = RuleChain.outerRule(new LoggingRule("outer rule"))
.around(new LoggingRule("middle rule"))
.around(new LoggingRule("inner rule"));
@Test
public void test() {}
}
Wenn man diesen Test ausführt, erhält man folgende Ausgabe:
starting outer rule starting middle rule starting inner rule finished inner rule finished middle rule finished outer rule
Die erste Regel (outer rule) umschließt also die mittlere (middle rule) und diese wiederum die dritte und letzte (inner rule).
Schreib deine eigenen Regeln!
Warum sollte man Rules verwenden? Ein großer Pluspunkt von Rules ist ihre Wiederverwendbarkeit. Sie ermöglichen häufig benutzten Code, der bisher in @Before/@After-Methoden oder einer Testoberklasse stand, in eine eigene TestRule-Klasse auszulagern, die nur eine Verantwortlichkeit hat.
Ein weiterer Vorteil ist die Kombinierbarkeit von Rules. Wie wir in diesem Artikel gesehen haben, lassen sich beliebig viele Regeln in einem Test verwenden, sowohl auf Klassen- als auch auf Methodenebene. Viele Dinge, für die es in der Vergangenheit eines eigenen Test Runners bedurfte, lassen sich jetzt mit Rules implementieren. Da man immer nur einen Test Runner aber beliebig viele Rules verwenden kann, stehen einem deutlich mehr Möglichkeiten offen.
Rules sind die Umsetzung von Delegation statt Vererbung für Unittests. Wo früher Testklassenhierarchien mit Utility-Methoden gewuchert sind, kann man jetzt auf einfache Art und Weise verschiedene Rules kombinieren.
Die vorgestellten, konkreten Rules demonstrieren lediglich die Vielfältigkeit der Einsatzmöglichkeiten. Eigene Regeln zu schreiben ist Dank der zur Verfügung gestellten Templateklassen einfach. Erst diese Erweiterbarkeit macht Rules zu einem wirklichen Novum.
Die Macher von JUnit setzen jedenfalls für die Zukunft von JUnit voll auf den Einsatz und die Erweiterung von Rules. Kent Beck schreibt darüber in seinem Blog [7]: „Maybe once every five years unsuspectedly powerful abstractions drop out of a program with no apparent effort.”
Links & Literatur
Primitive Matt(ch)ers?
The Hamcrest project provides a large number of matchers, i.e. declaratively defined predicates. Prominent uses of these matchers include testing and mocking libraries like JUnit and jMock, respectively.
How to use them?
@Test
public void onePlusOneIsTwo() {
assertThat(1 + 1, is(2));
}
While the above example is simple, it demonstrates one of the benefits of using assertThat() and Hamcrest matchers: assertions become very readable. Unfortunately, you often have to rely on a questionable Java mechanism: auto boxing/unboxing.
Auto boxing and unboxing have been introduced in Java 5 to ease the use of primitive types and their counterparts: real objects (a.k.a. reference types). However, especially unboxing can lead to hidden NullPointerExceptions and thus is discouraged by many developers. For details see Autoboxing is Evil by Nicole and Andreas.
For this reason, the Eclipse Java compiler optionally shows warnings whenever boxing or unboxing occurs. While it is certainly a good idea to enable this warning, it also puts markers on code that is perfectly sane, like the test case above. To prevent un-/boxing and use matchers at the same time, one can go back to pre-Java 5 times and convert the primitive literals explicitly:
@Test
public void onePlusOneIsTwo() {
assertThat(Integer.valueOf(1 + 1), is(Integer.valueOf(2)));
}
However, this is not readable anymore!
Why do we need boxing in the first place?
When using assertThat() we need boxing for two reasons. First, there is no definition of assertThat() for primitive types, only for reference types:
<T> void assertThat(T actual, Matcher<T> matcher)
Well, why don’t we overload assertThat() with separate method definitions for each primitive type, you might say.
void assertThat(int actual, Matcher<int> matcher)
Java’s generics do not allow primitive types like int as type arguments, only reference types are allowed.
Hmm, anything we can do?
Yes! So how about
void assertThat(int actual, Matcher<Integer> matcher) {
assertThat(Integer.valueOf(actual), matcher);
}
Without using auto boxing, we can now write:
@Test
public void onePlusOneIsTwo() {
assertThat(1 + 1, is(Integer.valueOf(2)));
}
That solves half of our problem: We got rid of the first boxing by overloading assertEquals(). However, we still need to explicity convert our int to Integer when calling the matcher factory method. Thus, we also need to overload the is() method:
Matcher<Integer> is(int value) {
return is(Integer.valueOf(value));
}
Problem solved! Really? Obviously, it requires a lot of work do define extra matcher factory methods for every combination of primitive type and matcher.
Solution: generate it!
Hamcrest already allows to generate matcher libraries, i.e. classes that collect all static factory methods for easy access at a single entry point. So, we could simply generate extra methods for every matcher. To stay with the example from above, if we have an unrestricted Matcher<T>, we would generate the following eight overloading methods:
Matcher<Byte> is(byte value) {
return is(Byte.valueOf(value));
}
Matcher<Short> is(short value) {
return is(Short.valueOf(value));
}
Matcher<Integer> is(int value) {
return is(Integer.valueOf(value));
}
Matcher<Long> is(long value) {
return is(Long.valueOf(value));
}
Matcher<Float> is(float value) {
return is(Float.valueOf(value));
}
Matcher<Double> is(double value) {
return is(Double.valueOf(value));
}
Matcher<Boolean> is(boolean value) {
return is(Boolean.valueOf(value));
}
Matcher<Character> is(char value) {
return is(Character.valueOf(value));
}
A matcher declaration which uses the object representation of a primitive type, e.g. Matcher<Integer>, is a special case that is even more simple. We would only need to generate a single extra method, such as
Matcher<Integer> zero(int value) {
return zero(Integer.valueOf(value));
}
Summing up, it would be great if Hamcrest provided built-in support for primitive types.
Update
My proposal was rejected by the Hamcrest maintainers
Project Usus on e4!
Combining SuiteBuilder and ClasspathSuite: Mo’ Power!
In a recent commit to JUnit Kent Beck and David Saff have added an “alpha-ready implementation of SuiteBuilder”. As Kent Beck previously described in a blog post, the idea behind the SuiteBuilder runner is to use annotations on fields instead of annotations on classes.
Limitations of regular test suites
While an annotation can take parameters the arguments must be literals, e.g. constant String values or class literals. For example, the classic Suite runner is configured using the @SuiteClasses annotation that takes an array of class literals, i.e. the test classes to be run:
@RunWith(Suite.class)
@SuiteClasses({
SomeTest.class,
YetAnotherTest.class
})
public class AllTests {}
Literals have a severe limitation: they must be know at compile-time! Thus, when using the Suite runner, there was no way of determining the classes to run by any other means such as scanning the current classpath.
ClasspathSuite to the rescue
For this original purpose, Johannes Link created the ClasspathSuite runner. Its basic usage is very simple: just specify it using the @RunWith annotation. In addition, you can also include test classes in JAR files, filter by class names or types, and so on:
@RunWith(ClasspathSuite.class)
@IncludeJars(true)
@ClassnameFilters({".*Test", "!.*AllTests"})
@BaseTypeFilter(MyBaseTest.class)
public class AllTests {}
However, the ClasspathSuite does not support JUnit’s categories as mentioned in an earlier blog post. While it could certainly be extended to support the Category-related annotations @IncludeCategory and @ExcludeCategory, the SuiteBuilder offers a more flexible alternative.
Introducing SuiteBuilder
The SuiteBuilder runner is similar to the Suite runner, but reads the test classes it is supposed to run from a field of the suite class annotated with @Classes. The field can be freely initialized to hold an implementation of the SuiteBuilder.Classes.Value interface which simply wraps a collection of classes. E.g., the first example can be rewritten using the SuiteBuilder:
@RunWith(SuiteBuilder.class)
public class AllTests {
@Classes
public Listed classes =
new Listed(SomeTest.class, YetAnotherTest.class);
}
In addition, you can filter the resulting test runners by annotating a field of type SuiteBuilder.RunnerFilter.Value with @RunnerFilter. For example, the latest commit included a CategoryFilter that filters tests by category:
@RunWith(SuiteBuilder.class)
public class OnlyYes {
@Classes
public Listed classes =
new Listed(SomeTest.class, YetAnotherTest.class);
@RunnerFilter
public CategoryFilter filter = CategoryFilter.include(Yes.class);
}
Putting the pieces together
So what? Well, instead of specifying the classes explicitly you could employ the capabilities of the ClasspathSuite to determine the test classes dynamically. For this purpose, I have written a small wrapper around Johannes Links’ ClasspathSuite. The above example can thus be rewritten without explicitly specifying the test classes:
@RunWith(SuiteBuilder.class)
public class OnlyYes {
@Classes
public InClasspath classes = new InClasspath();
@RunnerFilter
public CategoryFilter filter = CategoryFilter.include(Yes.class);
}
The wrapper offers the same flexibility as the ClasspathSuite, e.g.:
@RunWith(SuiteBuilder.class)
public class OnlyYes {
@Classes
public InClasspath classes = new InClasspath().includingJars()
.filteredBy(".*Test", "!.*AllTests")
.includingOnlySubclassesOf(MyBaseTest.class);
@RunnerFilter
public CategoryFilter filter = CategoryFilter.include(Yes.class);
}
While I will look into how this can be integrated into JUnit or ClasspathSuite feel free to contact me if you are interested in the source code of the InClasspath class.
Update
I am currently working on integrating ClasspathSuite and InClasspath into core JUnit… In the meantime, you can take a look at the code on GitHub.
Applying DRY to JUnit Categories
Long awaited, JUnit 4.8 introduced support for categorizing test cases.
A category marker is simply a class or interface, e.g.
public interface SlowTests {}
Tests can be marked using the @Category annotation:
public class A {
@Test
public void a() {}
@Category(SlowTests.class)
@Test
public void b() {}
}
The annotation works both on methods and classes:
@Category(SlowTests.class)
public class B {
@Test
public void c() {}
}
Test suites that include or exclude the SlowTests category are defined by specifying the Categories runner and using the @ExcludeCategory or @IncludeCategory annotation, respectively:
@RunWith(Categories.class)
@SuiteClasses( { A.class, B.class })
@ExcludeCategory(SlowTests.class)
public class AllFastTests extends AllTests {}
@RunWith(Categories.class)
@SuiteClasses( { A.class, B.class })
@IncludeCategory(SlowTests.class)
public class AllSlowTests extends AllTests {}
In this example, AllFastTests would execute only A.a while AllSlowTests would ignore A.a but run A.b and B.c.
However, there is a major issue in the above suite declarations: they violate the DRY principle. Both test suites list all test classes in the @SuiteClasses annotation. While it seems feasible to maintain the list of test classes at two locations for a small number of classes, it certainly is not a viable option in a real-world setting, especially when there are multiple categories.
Fortunately, there is a simple solution: use inheritance. You can define the list of test classes once in a normal
test suite …
@RunWith(Suite.class)
@SuiteClasses( { A.class, B.class })
public class AllTests {}
… and declare subclasses that filter the list of classes by category:
@RunWith(Categories.class)
@ExcludeCategory(SlowTests.class)
public class AllFastTests extends AllTests {}
@RunWith(Categories.class)
@IncludeCategory(SlowTests.class)
public class AllSlowTests extends AllTests {}
Generic matcher pitfalls
Using Hamcrest matchers in combination with assertThat allows for more fluid specification of JUnit assertions.
Recently, while working on the backend of Project Usus, we needed a simple matcher, that would test whether a given set is empty. At the time, we reused a set matcher we had already written a few minutes earlier.
Today, I had another look at the pre-defined matchers that come with Hamcrest and found the empty() matcher in org.hamcrest.Matchers. Since I’m not concerned with the actual implementation (at least for now), I’ll just give you the factory method:
@Factory
public static <E> Matcher<Collection<E>> empty() {
return new IsEmptyCollection<E>();
}
Great, I thought. So I readily changed our tests to use the pre-defined matcher…
assertThat(new TreeSet<String>(), empty());
However, this yielded a compile error because the compiler could not infer the type parameter of the method. It did work when stating the type parameter of the static method explicitly:
assertThat(new TreeSet<String>(), Matchers.<String>empty());
But that looked horrible. My first shot was to define an own factory method…
@Factory
public static <E> Matcher<Collection<E>> emptyOf(Class<E> clazz) {
return new IsEmptyCollection<E>();
}
…that can be used like this:
assertThat(new TreeSet<String>(), emptyOf(String.class));
I was still not very pleased with the solution. Even more since it does not matter at all what kind of objects are inside the collection to determine whether it is empty. After playing around for a little while I came up with this solution:
public class IsEmptyCollection extends TypeSafeMatcher<Collection<?>> {
@Override
protected boolean matchesSafely(Collection<?> collection) {
return collection.isEmpty();
}
public void describeTo(Description description) {
description.appendText("empty");
}
@Factory
public static Matcher<Collection<?>> empty() {
return new IsEmptyCollection();
}
}
In conclusion, I think it is not trivial to write usable generic matchers. Therefore, avoid generics when you don’t need them!
Experimenting with Theories
The very first 4.x release of JUnit contained support for custom test runners. Moreover, it came with the Parameterized test runner that allows to execute the test cases in a test class against a collection of values, i.e. parameters.
The example that comes with the Javadoc of the Parameterized class tests an imaginary Fibonacci calculator for a number of data points:
@RunWith(Parameterized.class)
public class FibonacciParameterizedTest {
@Parameters
public static List<Object[]> data() {
return Arrays.asList(new Object[][] { { 0, 0 }, { 1, 1 },
{ 2, 1 }, { 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 } });
}
private final int input;
private final int expected;
public FibonacciParameterizedTest(int input, int expected) {
this.input = input;
this.expected = expected;
}
@Test
public void test() {
assertEquals(expected, Fibonacci.compute(input));
}
}
JUnit 4.4 introduced Theories. A theory is an abstraction of a concrete test scenario, i.e. while a test specifies the behavior in one particular case, a theory captures more than a single scenario but is usually not as detailed in its assertions.
When using a Parameterized test you usually specify the input along with the expected output. Of course, you can do the same with a theory. However, theories allow for a complete different approach to testing the Fibonacci calculator.
E.g. a simple theory could state that for one of the seeds, i.e. 0 or 1, the same number is returned as result. Another theory could test the recurrence relation, i.e. that Fibonacci(n) always equals Fibonacci(n-1) + Fibonacci(n-2). In Java this can be written as:
@RunWith(Theories.class)
public class FibonacciTheories {
@DataPoints
public static int[] VALUES = { 0, 1, 2, 3, 4, 5, 6 };
@Theory
public void seeds(int n) {
assumeTrue(n <= 1);
assertEquals(n, compute(n));
}
@Theory
public void recurrence(int n) {
assumeTrue(n > 1);
assertEquals(compute(n - 1) + compute(n - 2), compute(n));
}
}
Even shorter yet:
@RunWith(Theories.class)
public class FibonacciTheories {
@Theory
public void seeds(@TestedOn(ints = { 0, 1 }) int n) {
assertEquals(n, compute(n));
}
@Theory
public void recurrence(@TestedOn(ints = { 2, 3, 4, 5, 6 }) int n) {
assertEquals(compute(n - 1) + compute(n - 2), compute(n));
}
}
So, which test is better? Actually, I am still undecided. While theories certainly look more elegant, a parameterized tests states its assumptions more clearly. The answer seems to be, as always: It depends.




