Hamcrest mit Matchern

Die Hamcrest 1.3 Javadoc-Dokumentation für die Matchers-Klasse enthält mehr Dokumentation für einige Methoden dieser Klasse als in Hamcrest 1.2 verfügbar. Zum Beispiel haben die vier überladenen Include-Methoden eine aussagekräftigere Javadoc-Dokumentation, wie in den beiden als nächstes gezeigten Snapshots des Vergleichsbildschirms gezeigt.

Obwohl man herausfinden kann, wie die "enthaltenen" Matcher funktionieren, indem man sie einfach ausprobiert, erleichtert das Javadoc in Hamcrest 1.3 das Lesen ihrer Funktionsweise. Die meisten Java-Entwickler denken wahrscheinlich an ein Verhalten wie String.contains (CharSequence) oder Collection.contains (Object), wenn sie an eine contains()Methode denken . Mit anderen Worten, die meisten Java-Entwickler denken wahrscheinlich, dass "enthält" beschreibt, ob der String / die Sammlung die bereitgestellten Zeichen / Objekte neben anderen möglichen Zeichen / Objekten enthält. Für Hamcrest-Matcher hat "enthält" jedoch eine viel spezifischere Bedeutung. Da die Hamcrest 1.3-Dokumentation viel klarer macht, reagieren die "enthält" -Matcher viel empfindlicher auf die Anzahl der Elemente und die Reihenfolge der Elemente, die an diese Methoden übergeben werden.

Meine hier gezeigten Beispiele verwenden JUnit und Hamcrest. Es ist wichtig zu betonen, dass die JAR-Datei von Hamcrest im Klassenpfad der Komponententests vor der JAR-Datei von JUnit angezeigt werden muss. Andernfalls muss ich die "spezielle" JAR-Datei von JUnit verwenden, die für die Verwendung mit der eigenständigen Hamcrest-JAR erstellt wurde. Durch die Verwendung eines dieser Ansätze werden der NoSuchMethodError und andere Fehler (erfolgreich als org.hamcrest.Matcher.describeMismatch-Fehler) vermieden, die sich aus nicht übereinstimmenden Versionen von Klassen ergeben. Ich habe über diese JUnit / Hamcrest-Nuance im Blog-Beitrag Moving Beyond Core Hamcrest in JUnit geschrieben.

Die nächsten beiden Screenshots zeigen die Ergebnisse (wie in NetBeans 7.3 gezeigt) der Unit-Test-Code-Snippets, die ich später im Blog zeige, um Hamcrest mit Matchern zu demonstrieren. Die Tests sollten einige Fehler aufweisen (7 Tests bestanden und 4 Tests fehlgeschlagen), um deutlich zu machen, wo Hamcrest-Matcher möglicherweise nicht wie erwartet funktionieren, ohne den Javadoc zu lesen. Das erste Bild zeigt nur 5 bestandene Tests, 2 fehlgeschlagene Tests und 4 fehlerhafte Tests. Dies liegt daran, dass JUnit vor Hamcrest im Klassenpfad "Test Libraries" des NetBeans-Projekts aufgeführt ist. Das zweite Bild zeigt die erwarteten Ergebnisse, da die Hamcrest-JAR vor der JUnit-JAR im Klassenpfad "Test Libaries" des Projekts auftritt.

Für die Zwecke dieser Demonstration muss eine einfache erfundene Klasse getestet werden. Der Quellcode für diese MainKlasse wird als nächstes angezeigt.

Main.java

package dustin.examples; import java.util.Collections; import java.util.HashSet; import java.util.Set; /** * Main class to be unit tested. * * @author Dustin */ public class Main { /** Uses Java 7's diamond operator. */ private Set strings = new HashSet(); public Main() {} public boolean addString(final String newString) { return this.strings.add(newString); } public Set getStrings() { return Collections.unmodifiableSet(this.strings); } } 

Nachdem die zu testende Klasse gezeigt wurde, ist es jetzt an der Zeit, einige JUnit-basierte Tests mit Hamcrest-Matchern zu erstellen. Insbesondere sollen die Tests sicherstellen, dass Zeichenfolgen, die über die addString(String)Methode der Klasse hinzugefügt wurden, zugrunde liegen Setund über die getStrings()Methode zugänglich sind . Die als nächstes gezeigten Unit-Test-Methoden zeigen, wie Hamcrest-Matcher angemessen verwendet werden, um festzustellen, ob hinzugefügte Strings im Basiswert der Klasse enthalten sindSet

Die Verwendung von Hamcrest enthält () Matcher mit einer einzelnen Zeichenfolge in Set Works

 /** * This test will pass because there is only a single String and so it will * contain that single String and order will be correct by implication. */ @Test public void testAddStringAndGetStringsWithContainsForSingleStringSoWorks() { final Main subject = new Main(); final boolean resultJava = subject.addString("Java"); final Set strings = subject.getStrings(); assertThat(strings, contains("Java")); } 

Der oben gezeigte Komponententest besteht, weil der Seteinzige nur einen String enthält und daher die Reihenfolge und Anzahl der mit dem containsMatcher getesteten Strings übereinstimmt.

Die Verwendung von Hamcrest enthält mit der gleichen Anzahl von Elementen funktioniert, wenn die Reihenfolge übereinstimmt

 /** * The "contains" matcher expects exact ordering, which really means it should * not be used in conjunction with {@code Set}s. Typically, either this method * will work and the method with same name and "2" on end will not work or * vice versa. */ @Test public void testAddStringAndGetStringsWithContainsForMultipleStringsNotWorks1() { final Main subject = new Main(); final boolean resultJava = subject.addString("Java"); final boolean resultGroovy = subject.addString("Groovy"); final Set strings = subject.getStrings(); assertThat(strings, contains("Java", "Groovy")); } /** * The "contains" matcher expects exact ordering, which really means it should * not be used in conjunction with {@code Set}s. Typically, either this method * will work and the method with same name and "1" on end will not work or * vice versa. */ @Test public void testAddStringAndGetStringsWithContainsForMultipleStringsNotWorks2() { final Main subject = new Main(); final boolean resultJava = subject.addString("Java"); final boolean resultGroovy = subject.addString("Groovy"); final Set strings = subject.getStrings(); assertThat(strings, contains("Groovy", "Java")); } 

Die beiden oben gezeigten Beispiel-Unit-Tests und die daraus resultierende Ausgabe der Ausführung dieses Tests, wie im vorherigen Screenshot gezeigt, zeigen, dass die Anzahl der Argumente für den contains()Matcher mit der Anzahl der Strings in der getesteten Sammlung übereinstimmt funktioniert möglicherweise , wenn die getesteten Elemente in genau derselben Reihenfolge wie die Elemente in der Sammlung vorliegen. Bei einer ungeordneten SetReihenfolge kann man sich nicht auf diese Reihenfolge verlassen und contains()ist daher wahrscheinlich kein guter Matcher für einen Komponententest mit Setmehr als einem Element.

Die Verwendung von Hamcrest Contains mit einer unterschiedlichen Anzahl von Elementen funktioniert nie

 /** * Demonstrate that contains will NOT pass when there is a different number * of elements asked about contains than in the collection. */ @Test public void testAddStringAndGetStringsWithContainsNotWorksDifferentNumberElements1() { final Main subject = new Main(); final boolean resultJava = subject.addString("Java"); final boolean resultGroovy = subject.addString("Groovy"); final Set strings = subject.getStrings(); assertThat(strings, contains("Java")); } /** * Demonstrate that contains will NOT pass when there is a different number * of elements asked about contains than in the collection even when in * different order. */ @Test public void testAddStringAndGetStringsWithContainsNotWorksDifferentNumberElements2() { final Main subject = new Main(); final boolean resultJava = subject.addString("Java"); final boolean resultGroovy = subject.addString("Groovy"); final Set strings = subject.getStrings(); assertThat(strings, contains("Groovy")); } 

Wie die JUnit-Testergebnisse zeigen, bestehen diese beiden Komponententests nie, da die Anzahl der Elemente, auf die im getestet Setwird, geringer ist als die Anzahl der Elemente im Set. Mit anderen Worten, dies beweist, dass der contains()Matcher nicht einfach auf ein bestimmtes Element in einer Sammlung testet, sondern auf alle vorhandenen Elemente in der angegebenen Reihenfolge. Dies kann in einigen Fällen zu einschränkend sein, daher gehe ich jetzt zu einigen anderen Übereinstimmungen über, die Hamcrest bereitstellt, um festzustellen, ob ein Element in einer bestimmten Sammlung enthalten ist.

Verwenden von Hamcrests enthältInAnyOrder () Matcher

Der containsInAnyOrderMatcher ist nicht so streng wie der contains()Matcher: Er ermöglicht, dass getestete Elemente in beliebiger Reihenfolge innerhalb der enthaltenen Sammlung übergeben werden.

 /** * Test of addString and getStrings methods of class Main using Hamcrest * matcher containsInAnyOrder. */ @Test public void testAddStringAndGetStringsWithContainsInAnyOrder() { final Main subject = new Main(); final boolean resultJava = subject.addString("Java"); final boolean resultCSharp = subject.addString("C#"); final boolean resultGroovy = subject.addString("Groovy"); final boolean resultScala = subject.addString("Scala"); final boolean resultClojure = subject.addString("Clojure"); final Set strings = subject.getStrings(); assertThat(strings, containsInAnyOrder("Java", "C#", "Groovy", "Scala", "Clojure")); } /** * Use containsInAnyOrder and show that order does not matter as long as * all entries provided are in the collection in some order. */ @Test public void testAddStringAndGetStringsWithContainsInAnyOrderAgain() { final Main subject = new Main(); final boolean resultJava = subject.addString("Java"); final boolean resultGroovy = subject.addString("Groovy"); final Set strings = subject.getStrings(); assertThat(strings, containsInAnyOrder("Java", "Groovy")); assertThat(strings, containsInAnyOrder("Groovy", "Java")); } 

Die beiden unmittelbar oben gezeigten Unit-Tests bestehen beide, obwohl die getesteten Strings dem containsInAnyOrder()Matcher in einer anderen Reihenfolge zur Verfügung gestellt werden, als sie für beide Sammlungen vorhanden sein könnten. Für den weniger strengen containsInAnyOrder()Matcher müssen jedoch weiterhin alle Elemente der enthaltenen Auflistung angegeben werden, damit sie übergeben werden können. Der folgende Komponententest besteht nicht, da diese Bedingung nicht erfüllt ist.

 /** * This will fail because containsInAnyOrder requires all items to be matched * even if in different order. With only one element being tried and two * elements in the collection, it will still fail. In other words, order * does not matter with containsInAnyOrder, but all elements in the collection * still need to be passed to the containsInAnyOrder matcher, just not in the * exact same order. */ @Test public void testAddStringAndGetStringsWithContainsInAnyOrderDiffNumberElements() { final Main subject = new Main(); final boolean resultJava = subject.addString("Java"); final boolean resultGroovy = subject.addString("Groovy"); final Set strings = subject.getStrings(); assertThat(strings, containsInAnyOrder("Java")); } 

Hamcrest hasItem () und hasItems () Matcher arbeiten als Sounds

Wie in den nächsten beiden Unit-Test-Methoden gezeigt (beide bestehen), testet der Hamcrest hasItem()(für einen einzelnen Artikel) und hasItems(für mehrere Artikel) erfolgreich, ob eine Sammlung einen oder mehrere angegebene Artikel enthält, ohne Rücksicht auf Reihenfolge oder Nummer von bestimmten Gegenständen. Dies funktioniert wirklich eher so, als ob die meisten Java-Entwickler es gewohnt sind, beim Arbeiten mit Strings und Sammlungen zu "enthalten".

 /** * Demonstrate hasItem() will also work for determining a collection contains * a particular item. */ @Test public void testAddStringAndGetStringsWithHasItem() { final Main subject = new Main(); final boolean resultJava = subject.addString("Java"); final boolean resultGroovy = subject.addString("Groovy"); final Set strings = subject.getStrings(); assertThat(strings, hasItem("Groovy")); assertThat(strings, hasItem("Java")); } /** * Demonstrate that hasItems works for determining that a collection has one * or more items and that the number of items and the order of the items * is not significant in determining pass/failure. */ @Test public void testAddStringAndGetStringsWithHasItems() { final Main subject = new Main(); final boolean resultJava = subject.addString("Java"); final boolean resultGroovy = subject.addString("Groovy"); final Set strings = subject.getStrings(); assertThat(strings, hasItems("Groovy", "Java")); assertThat(strings, hasItems("Java", "Groovy")); assertThat(strings, hasItems("Groovy")); assertThat(strings, hasItems("Java")); } 

Hamcrest isIn () Matcher testet die Eindämmung aus anderer Richtung

Die gerade diskutierten hasItem()und hasItems()Matcher sind weniger streng als contains()und sogar noch weniger streng als containsInAnyOrder()und sind oft das, was man will, wenn man einfach sicherstellen will, dass sich ein oder mehrere Artikel irgendwo in einer Sammlung befinden, ohne sich um die Reihenfolge des Artikels in dieser Sammlung oder die andere mögliche zu kümmern Artikel sind in dieser Sammlung. Eine andere Möglichkeit, Hamcrest zu verwenden, um dieselbe Beziehung zu bestimmen, jedoch aus der entgegengesetzten Perspektive, ist die Verwendung von isInMatcher. Der isInMatcher bestimmt, ob sich ein Artikel irgendwo in der Sammlung befindet, die dem Matcher zur Verfügung gestellt wurde, ohne Rücksicht auf die Reihenfolge des Artikels in der Sammlung oder ob andere Artikel in der Sammlung enthalten sind oder nicht.

 /** * Use isIn matcher to test individual element is in provided collection. */ @Test public void testAddStringAndGetStringsWithIsIn() { final Main subject = new Main(); final boolean resultJava = subject.addString("Java"); final boolean resultGroovy = subject.addString("Groovy"); final Set strings = subject.getStrings(); assertThat("Groovy", isIn(strings)); assertThat("Java", isIn(strings)); }