Nur noch assertThat in Unit Tests

Mit JUnit 4.4 wurde die Hamcrest Bibliothek über die unscheinbare Methode assertThat Bestandteil des Test Frameworks. Obwohl schon einige Zeit vergangen ist, haben viele Entwickler das Potential dieser Ergänzung noch nicht entdeckt.

Bislang werden in den Unit Test die aktuellen Werte gegen die zu erwartenden Werte geprüft. Am beliebtesten sind da die Methoden assertEquals und assertTrue.

assertEquals("Hello World", object.toString());
assertTrue(file.exists());

In der ersten Zeile wird geprüft ob das Resultat der Methode toString mit dem Erwartungswert übereinstimmt und in der zweiten Zeile wird geprüft ob der übergebene Ausdruck true zurückliefert. Neben diesen beiden Methoden gibt es noch eine lange Liste weiterer Methoden, die unter anderen Arrays und Collections auf Gleichheit, Objekte auf Identität und reelle Zahlen auf einen Näherungswert testen. Zur besseren Lesbarkeit werden die assert Methoden über einen statischen Import eingefügt und der Klassenname Assert kann entfallen.

Der Ansatz bei der assertThat Methode ist ein anderer, hier wird ein Matcher gegen das aktuelle Objekt ausgewertet. Die Schnittstelle ist sehr flexibel und erlaub eine Vielzahl von Prüfungen auf dem Objekt.

assertThat(object.toString(), is(equalTo("Hello World"));
assertThat(file, exists());

Bei dieser Methode steht das zu testende Objekt links und der Ausdruck, der erfüllt werden muss auf der linken Seite.  In der ersten Zeile wird mit dem equalTo Matcher geprüft, ob die Methode den Ausdruck „Hello World“ liefert und in den zweiten Zeile wird mit einem selbstgeschriebenen Matcher die Existenz der Datei geprüft.  Wenn man sich eine bessere Lesbarkeit wünscht, kann man wie oben geschehen den Matcher is einfügen.

Neben der guten Lesbarkeit und der Erweiterbarkeit trumpft der Matcher Ansatz aber noch mit einigen weiteren Vorteilen auf. Die Matcher sind typsicher und miteinander kombinierbar. Im folgenden sind die assertEquals und die assertThat im fehlerhaften Einsatz. Während  assertEquals unerkannt einen falschen Vergleich durchführt, kann der assertThat Ausdruck erst gar nicht kompilieren.

assertEquals(object.hashCode(), "HelloWorld");
assertThat(object.hashCode(), equalTo("Hello World"));

Die Matcher können auf viele Arten miteinander kombiniert werden. Eine sehr einfache Kombination ist die Verwendung des not Matchers oder die Anwendung eines Matchers auf eine Liste.

assertThat(file, not(exists()));
assertThat(listOfFiles, everyItem(exists());
assertThat(listOfFiles, hasItem(exists());

In der ersten Zeile wird geprüft, ob die Datei nicht existiert und in den beiden anderen Zeilen, ob jede Datei in der Liste oder mindestens eine Datei existiert. Häufig müssen mehrere Bedingungen auf einem Object geprüft werden, auch solche Prüfungen sind mit den bereitgestellten Matchern leicht formulierbar:

assertThat(marxBrother, anyOf(chico, harpo, groucho, gummo, zeppo));
assertThat(subject, either(pinky).or(brain));

Und wem die vorhandenen Matcher nicht ausreichen, der kann sich recht einfach eigene schreiben. Der oben vorgestellte exists Matcher könnte wie folgt formuliert werden.

public static Matcher<Path> exists() {
  return new FeatureMatcher<Path, Boolean>(Matchers.is(true), "exists", "exists") {
    @Override
    protected Boolean featureValueOf(Path actual) {
      return Files.exists(actual);
    }
  };
}

Die statische Methode exists liefert einen FeatureMatcher, der für ein Path Objekt true zurückliefert, wenn die Datei existiert, ansonsten false.

Abschließend kann man allen Entwicklern die Verwendung der assertThat Methode und des Hamcrest Framework nur wärmsten ans Herz legen. Die Kombinierbarkeit und Erweiterbarkeit der Matcher führt zu übersichtlichen, kompakt formulierten Unit Test, die Entwicklungs- und Einarbeitungszeit sparen.