Hamcrest Custom Matchers

1. Einleitung

Neben integrierten Matchern bietet Hamcrest auch Unterstützung beim Erstellen benutzerdefinierter Matcher.

In diesem Tutorial werden wir uns genauer ansehen, wie man sie erstellt und verwendet. In diesem Artikel finden Sie einen Einblick in die verfügbaren Matcher.

2. Benutzerdefinierte Matcher einrichten

Um Hamcrest zu erhalten, müssen wir unserer pom.xml die folgende Maven-Abhängigkeit hinzufügen :

 org.hamcrest java-hamcrest 2.0.0.0 test 

Die neueste Hamcrest-Version finden Sie auf Maven Central.

3. Einführung in TypeSafeMatcher

Bevor Sie mit unseren Beispielen beginnen, ist es wichtig, die Klasse TypeSafeMatcher zu verstehen . Wir müssen diese Klasse erweitern, um einen eigenen Matcher zu erstellen.

TypeSafeMatcher ist eine abstrakte Klasse, daher müssen alle Unterklassen die folgenden Methoden implementieren:

  • matchSafely (T t) : enthält unsere Matching-Logik
  • descriptionTo (Beschreibung Beschreibung) : Passt die Nachricht an, die der Client erhält, wenn unsere Übereinstimmungslogik nicht erfüllt ist

Wie wir in der ersten Methode sehen können, ist TypeSafeMatcher parametrisiert, daher müssen wir einen Typ deklarieren, wenn wir ihn verwenden. Dies ist der Typ des Objekts, das wir testen.

Lassen Sie uns dies klarer machen, indem wir uns unser erstes Beispiel im nächsten Abschnitt ansehen.

4. Erstellen Sie den onlyDigits Matcher

Für unseren ersten Anwendungsfall erstellen wir einen Matcher, der true zurückgibt, wenn ein bestimmter String nur Ziffern enthält.

Daher sollten nur auf "123" angewendete Ziffern " true " zurückgeben , während " hello1 " und " bye " false zurückgeben sollten.

Lass uns anfangen!

4.1. Matcher-Erstellung

Zunächst erstellen wir eine Klasse, die TypeSafeMatcher erweitert :

public class IsOnlyDigits extends TypeSafeMatcher { @Override protected boolean matchesSafely(String s) { // ... } @Override public void describeTo(Description description) { // ... } }

Bitte beachten Sie, dass das zu testende Objekt ein Text ist und wir unsere Unterklasse von TypeSafeMatcher mit der Klasse String parametrisieren .

Jetzt können wir unsere Implementierung hinzufügen:

public class IsOnlyDigits extends TypeSafeMatcher { @Override protected boolean matchesSafely(String s) { try { Integer.parseInt(s); return true; } catch (NumberFormatException nfe){ return false; } } @Override public void describeTo(Description description) { description.appendText("only digits"); } }

Wie wir sehen können, matchesSafey versucht unser Eingang zu analysieren String in einen Integer . Wenn dies erfolgreich ist, wird true zurückgegeben . Wenn dies fehlschlägt, wird false zurückgegeben . Es reagiert erfolgreich auf unseren Anwendungsfall.

Auf der anderen Seite fügt descriptionTo einen Text hinzu, der unsere Erwartungen widerspiegelt. Wir werden sehen, wie sich dies als nächstes zeigt, wenn wir unseren Matcher verwenden.

Wir brauchen nur noch eine Sache, um unseren Matcher zu vervollständigen: eine statische Methode, um darauf zuzugreifen , sodass er sich wie der Rest der integrierten Matcher verhält.

Also werden wir so etwas hinzufügen:

public static Matcher onlyDigits() { return new IsOnlyDigits(); }

Und wir sind fertig! Lassen Sie uns im nächsten Abschnitt sehen, wie dieser Matcher verwendet wird.

4.2. Matcher-Verwendung

Um unseren brandneuen Matcher zu verwenden, erstellen wir einen Test :

@Test public void givenAString_whenIsOnlyDigits_thenCorrect() { String digits = "1234"; assertThat(digits, onlyDigits()); }

Und das ist es. Dieser Test wird übergeben , da die Eingabe String nur Ziffern enthält. Denken Sie daran , dass, um es ein wenig mehr lesbar, wir die Matcher verwenden können ist , dass fungiert als Wrapper über jeden anderen Matcher :

assertThat(digits, is(onlyDigits()));

Wenn wir denselben Test ausführen würden, jedoch mit der Eingabe "123ABC", würde die Ausgabemeldung lauten:

java.lang.AssertionError: Expected: only digits but: was "123ABC"

Hier sehen wir den Text, den wir an die descriptionTo- Methode angehängt haben . Wie wir vielleicht bemerkt haben, ist es wichtig, eine korrekte Beschreibung der im Test erwarteten Ergebnisse zu erstellen.

5. teilbar durch

Was wäre, wenn wir einen Matcher erstellen wollten, der definiert, ob eine Zahl durch eine andere Zahl teilbar ist? In diesem Szenario müssen wir einen der Parameter irgendwo speichern.

Mal sehen, wie wir das machen können:

public class IsDivisibleBy extends TypeSafeMatcher { private Integer divider; // constructors @Override protected boolean matchesSafely(Integer dividend) { if (divider == 0) { return false; } return ((dividend % divider) == 0); } @Override public void describeTo(Description description) { description.appendText("divisible by " + divider); } public static Matcher divisibleBy(Integer divider) { return new IsDivisibleBy(divider); } }

Ganz einfach, wir haben unserer Klasse gerade ein neues Attribut hinzugefügt und es während der Erstellung zugewiesen . Dann haben wir es einfach als Parameter an unsere statische Methode übergeben:

@Test public void givenAnEvenInteger_whenDivisibleByTwo_thenCorrect() { Integer ten = 10; Integer two = 2; assertThat(ten,is(divisibleBy(two))); } @Test public void givenAnOddInteger_whenNotDivisibleByTwo_thenCorrect() { Integer eleven = 11; Integer two = 2; assertThat(eleven,is(not(divisibleBy(two)))); }

Und das ist es! Wir haben unseren Matcher bereits mit mehr als einem Eingang!

6. Fazit

Hamcrest bietet Matcher, die die meisten Anwendungsfälle abdecken, mit denen sich Entwickler normalerweise beim Erstellen von Zusicherungen befassen müssen.

Wenn ein bestimmter Fall nicht behandelt wird, bietet Hamcrest außerdem Unterstützung beim Erstellen von benutzerdefinierten Matchern, die unter bestimmten Szenarien verwendet werden können - wie wir hier untersucht haben . Sie sind einfach zu erstellen und werden genau wie die in der Bibliothek enthaltenen verwendet.

Informationen zur vollständigen Implementierung dieser Beispiele finden Sie im GitHub-Projekt.