Testen mit Hamcrest

Testen mit Hamcrest

1. Überblick

Hamcrest ist das bekannte Framework für Unit-Tests im Java-Ökosystem. Es ist in JUnit gebündelt und verwendet einfach vorhandene Prädikate - sogenannte Matcher-Klassen -, um Aussagen zu treffen.

In diesem Tutorial lernen wirexplore the Hamcrest API und lernen, wie wir es nutzen können, um sauberere und intuitivere Komponententests für unsere Software zu schreiben.

2. Hamcrest Setup

Wir könnenHamcrest mit maven verwenden, indem wir unsererpom.xml-Datei die folgende Abhängigkeit hinzufügen:


    org.hamcrest
    hamcrest-all
    1.3

Die neueste Version dieser Bibliothek befindet sich immer inhere.

3. Ein Beispieltest

Hamcrest wird üblicherweise mitjunit und anderen Test-Frameworks verwendet, um Aussagen zu treffen. Anstatt die zahlreichenassert-Methoden vonjunitzu verwenden, verwenden wir nur die einzelneassertThat-Anweisung der API mit geeigneten Übereinstimmungen.

Schauen wir uns ein Beispiel an, in dem zweiStrings unabhängig vom Fall auf Gleichheit getestet werden. Dies sollte uns eine klare Vorstellung davon geben, wieHamcrest in eine Testmethode passt:

public class StringMatcherTest {

    @Test
    public void given2Strings_whenEqual_thenCorrect() {
        String a = "foo";
        String b = "FOO";
        assertThat(a, equalToIgnoringCase(b));
    }
}

In den folgenden Abschnitten werden wir uns einige andere gängige MatcherHamcrest-Angebote ansehen.

4. DerObject Matcher

Hamcrest bietet Matcher zum Erstellen von Zusicherungen für beliebige Java-Objekte.

Um zu behaupten, dass dietoString-Methode einesObject ein angegebenesString zurückgibt:

@Test
public void givenBean_whenToStringReturnsRequiredString_thenCorrect(){
    Person person=new Person("Barrack", "Washington");
    String str=person.toString();
    assertThat(person,hasToString(str));
}

Wir können auch überprüfen, ob eine Klasse eine Unterklasse einer anderen ist:

@Test
public void given2Classes_whenOneInheritsFromOther_thenCorrect(){
        assertThat(Cat.class,typeCompatibleWith(Animal.class));
    }
}

5. Der Bohnen-Matcher

Wir können den Bean-Matcher vonHamcrestverwenden, um die Eigenschaften einer Java-Bean zu überprüfen.

Nehmen Sie die folgendePerson-Bohne an:

public class Person {
    String name;
    String address;

    public Person(String personName, String personAddress) {
        name = personName;
        address = personAddress;
    }
}

Wir können überprüfen, ob die Bohne die Eigenschaft hat,name wie folgt:

@Test
public void givenBean_whenHasValue_thenCorrect() {
    Person person = new Person("example", 25);
    assertThat(person, hasProperty("name"));
}

Wir können auch überprüfen, obPerson die in New York initialisierte Eigenschaftaddresshat:

@Test
public void givenBean_whenHasCorrectValue_thenCorrect() {
    Person person = new Person("example", "New York");
    assertThat(person, hasProperty("address", equalTo("New York")));
}

Wir können auch überprüfen, ob zweiPerson Objekte mit denselben Werten erstellt wurden:

@Test
public void given2Beans_whenHavingSameValues_thenCorrect() {
    Person person1 = new Person("example", "New York");
    Person person2 = new Person("example", "New York");
    assertThat(person1, samePropertyValuesAs(person2));
}

6. DerCollection Matcher

Hamcrest bietet Matcher für die Überprüfung vonCollections.

Einfache Überprüfung, um festzustellen, ob einCollection leer ist:

@Test
public void givenCollection_whenEmpty_thenCorrect() {
    List emptyList = new ArrayList<>();
    assertThat(emptyList, empty());
}

So überprüfen Sie die Größe vonCollection:

@Test
public void givenAList_whenChecksSize_thenCorrect() {
    List hamcrestMatchers = Arrays.asList(
      "collections", "beans", "text", "number");
    assertThat(hamcrestMatchers, hasSize(4));
}

Wir können es auch verwenden, um zu behaupten, dass ein Array eine erforderliche Größe hat:

@Test
public void givenArray_whenChecksSize_thenCorrect() {
    String[] hamcrestMatchers = { "collections", "beans", "text", "number" };
    assertThat(hamcrestMatchers, arrayWithSize(4));
}

So überprüfen Sie, ob einCollection bestimmte Mitglieder enthält, unabhängig von der Reihenfolge:

@Test
public void givenAListAndValues_whenChecksListForGivenValues_thenCorrect() {
    List hamcrestMatchers = Arrays.asList(
      "collections", "beans", "text", "number");
    assertThat(hamcrestMatchers,
    containsInAnyOrder("beans", "text", "collections", "number"));
}

Um weiter zu behaupten, dass die Mitglieder vonCollectionin der angegebenen Reihenfolge sind:

@Test
public void givenAListAndValues_whenChecksListForGivenValuesWithOrder_thenCorrect() {
    List hamcrestMatchers = Arrays.asList(
      "collections", "beans", "text", "number");
    assertThat(hamcrestMatchers,
    contains("collections", "beans", "text", "number"));
}

So überprüfen Sie, ob ein Array ein einzelnes Element enthält:

@Test
public void givenArrayAndValue_whenValueFoundInArray_thenCorrect() {
    String[] hamcrestMatchers = { "collections", "beans", "text", "number" };
    assertThat(hamcrestMatchers, hasItemInArray("text"));
}

Für denselben Test können wir auch einen alternativen Matcher verwenden:

@Test
public void givenValueAndArray_whenValueIsOneOfArrayElements_thenCorrect() {
    String[] hamcrestMatchers = { "collections", "beans", "text", "number" };
    assertThat("text", isOneOf(hamcrestMatchers));
}

Oder wir können dasselbe mit einem anderen Matcher machen:

@Test
public void givenValueAndArray_whenValueFoundInArray_thenCorrect() {
    String[] array = new String[] { "collections", "beans", "text",
      "number" };
    assertThat("beans", isIn(array));
}

Wir können auch überprüfen, ob das Array bestimmte Elemente enthält, unabhängig von der Reihenfolge:

@Test
public void givenArrayAndValues_whenValuesFoundInArray_thenCorrect() {
    String[] hamcrestMatchers = { "collections", "beans", "text", "number" };
      assertThat(hamcrestMatchers,
    arrayContainingInAnyOrder("beans", "collections", "number",
      "text"));
}

So überprüfen Sie, ob das Array bestimmte Elemente enthält, jedoch in der angegebenen Reihenfolge:

@Test
public void givenArrayAndValues_whenValuesFoundInArrayInOrder_thenCorrect() {
    String[] hamcrestMatchers = { "collections", "beans", "text", "number" };
    assertThat(hamcrestMatchers,
    arrayContaining("collections", "beans", "text", "number"));
}

Wenn unserCollection einMap, ist, können wir die folgenden Matcher in diesen jeweiligen Funktionen verwenden:

So überprüfen Sie, ob es einen bestimmten Schlüssel enthält:

@Test
public void givenMapAndKey_whenKeyFoundInMap_thenCorrect() {
    Map map = new HashMap<>();
    map.put("blogname", "example");
    assertThat(map, hasKey("blogname"));
}

und ein gegebener Wert:

@Test
public void givenMapAndValue_whenValueFoundInMap_thenCorrect() {
    Map map = new HashMap<>();
    map.put("blogname", "example");
    assertThat(map, hasValue("example"));
}

und schließlich ein gegebener Eintrag (Schlüssel, Wert):

@Test
public void givenMapAndEntry_whenEntryFoundInMap_thenCorrect() {
    Map map = new HashMap<>();
    map.put("blogname", "example");
    assertThat(map, hasEntry("blogname", "example"));
}

7. DerNumber Matcher

DieNumber-Matcher werden verwendet, um Zusicherungen für Variablen derNumber-Klasse durchzuführen.

So überprüfen Sie den Zustand vongreaterThan:

@Test
public void givenAnInteger_whenGreaterThan0_thenCorrect() {
    assertThat(1, greaterThan(0));
}

So überprüfen Sie den Zustand vongreaterThan oderequalTo:

@Test
public void givenAnInteger_whenGreaterThanOrEqTo5_thenCorrect() {
    assertThat(5, greaterThanOrEqualTo(5));
}

So überprüfen Sie den Zustand vonlessThan:

@Test
public void givenAnInteger_whenLessThan0_thenCorrect() {
    assertThat(-1, lessThan(0));
}

So überprüfen Sie den Zustand vonlessThan oderequalTo:

@Test
public void givenAnInteger_whenLessThanOrEqTo5_thenCorrect() {
    assertThat(-1, lessThanOrEqualTo(5));
}

So überprüfen Sie den Zustand voncloseTo:

@Test
public void givenADouble_whenCloseTo_thenCorrect() {
    assertThat(1.2, closeTo(1, 0.5));
}

Achten wir genau auf den letzten MatchercloseTo.. Das erste Argument, der Operand, ist dasjenige, mit dem das Ziel verglichen wird, und das zweite Argument ist die zulässige Abweichung vom Operanden.. Dies bedeutet, dass wenn Das Ziel ist Operand + Abweichung oder Operandenabweichung, dann besteht der Test.

8. Der Text Matcher

Die Aussage zuStrings wird durch die Text-Matcher vonHamcrest einfacher, übersichtlicher und intuitiver. Wir werden sie uns in diesem Abschnitt ansehen.

So überprüfen Sie, obString leer ist:

@Test
public void givenString_whenEmpty_thenCorrect() {
    String str = "";
    assertThat(str, isEmptyString());
}

So überprüfen Sie, obString leer odernull ist:

@Test
public void givenString_whenEmptyOrNull_thenCorrect() {
    String str = null;
    assertThat(str, isEmptyOrNullString());
}

So überprüfen Sie die Gleichheit von zweiStrings, während Sie Leerzeichen ignorieren:

@Test
public void given2Strings_whenEqualRegardlessWhiteSpace_thenCorrect() {
    String str1 = "text";
    String str2 = " text ";
    assertThat(str1, equalToIgnoringWhiteSpace(str2));
}

Wir können auch prüfen, ob eine oder mehrere Teilzeichenfolgen in einem bestimmtenString in einer bestimmten Reihenfolge vorhanden sind:

@Test
public void givenString_whenContainsGivenSubstring_thenCorrect() {
    String str = "calligraphy";
    assertThat(str, stringContainsInOrder(Arrays.asList("call", "graph")));
}

Schließlich können wir unabhängig vom Fall die Gleichheit von zweiStrings überprüfen:

@Test
 public void given2Strings_whenEqual_thenCorrect() {
    String a = "foo";
    String b = "FOO";
    assertThat(a, equalToIgnoringCase(b));
}

9. Die Core API

Die Kern-API vonHamcrestoll von Framework-Anbietern von Drittanbietern verwendet werden. Es bietet uns jedoch einige großartige Konstrukte, um unsere Unit-Tests besser lesbar zu machen, sowie einige Core-Matcher, die genauso einfach verwendet werden können.

Lesbarkeit mit dem Konstruktis auf einem Matcher:

@Test
public void given2Strings_whenIsEqualRegardlessWhiteSpace_thenCorrect() {
    String str1 = "text";
    String str2 = " text ";
    assertThat(str1, is(equalToIgnoringWhiteSpace(str2)));
}

Dasis-Konstrukt für einen einfachen Datentyp:

@Test
public void given2Strings_whenIsEqual_thenCorrect() {
    String str1 = "text";
    String str2 = "text";
    assertThat(str1, is(str2));
}

Negation mit demnot-Konstrukt auf einem Matcher:

@Test
public void given2Strings_whenIsNotEqualRegardlessWhiteSpace_thenCorrect() {
    String str1 = "text";
    String str2 = " texts ";
    assertThat(str1, not(equalToIgnoringWhiteSpace(str2)));
}

Dasnot-Konstrukt für einen einfachen Datentyp:

@Test
public void given2Strings_whenNotEqual_thenCorrect() {
    String str1 = "text";
    String str2 = "texts";
    assertThat(str1, not(str2));
}

Überprüfen Sie, ob einString eine bestimmte Unterzeichenfolge enthält:

@Test
public void givenAStrings_whenContainsAnotherGivenString_thenCorrect() {
    String str1 = "calligraphy";
    String str2 = "call";
    assertThat(str1, containsString(str2));
}

Überprüfen Sie, ob einString mit einer bestimmten Unterzeichenfolge beginnt:

@Test
public void givenAString_whenStartsWithAnotherGivenString_thenCorrect() {
    String str1 = "calligraphy";
    String str2 = "call";
    assertThat(str1, startsWith(str2));
}

Überprüfen Sie, ob einString mit einer bestimmten Unterzeichenfolge endet:

@Test
public void givenAString_whenEndsWithAnotherGivenString_thenCorrect() {
    String str1 = "calligraphy";
    String str2 = "phy";
    assertThat(str1, endsWith(str2));
}

Überprüfen Sie, ob zweiObjects von derselben Instanz sind:

@Test
public void given2Objects_whenSameInstance_thenCorrect() {
    Cat cat=new Cat();
    assertThat(cat, sameInstance(cat));
}

Überprüfen Sie, obObject eine Instanz einer bestimmten Klasse ist:

@Test
public void givenAnObject_whenInstanceOfGivenClass_thenCorrect() {
    Cat cat=new Cat();
    assertThat(cat, instanceOf(Cat.class));
}

Überprüfen Sie, ob alle Mitglieder einesCollectioneine Bedingung erfüllen:

@Test
public void givenList_whenEachElementGreaterThan0_thenCorrect() {
    List list = Arrays.asList(1, 2, 3);
    int baseCase = 0;
    assertThat(list, everyItem(greaterThan(baseCase)));
}

Überprüfen Sie, ob aString nichtnull ist:

@Test
public void givenString_whenNotNull_thenCorrect() {
    String str = "notnull";
    assertThat(str, notNullValue());
}

Verkettungsbedingungen zusammen, Test besteht, wenn das Ziel eine der Bedingungen erfüllt, ähnlich dem logischen ODER:

@Test
public void givenString_whenMeetsAnyOfGivenConditions_thenCorrect() {
    String str = "calligraphy";
    String start = "call";
    String end = "foo";
    assertThat(str, anyOf(startsWith(start), containsString(end)));
}

Kettenbedingungen zusammen, Test besteht nur, wenn das Ziel alle Bedingungen erfüllt, ähnlich dem logischen UND:

@Test
public void givenString_whenMeetsAllOfGivenConditions_thenCorrect() {
    String str = "calligraphy";
    String start = "call";
    String end = "phy";
    assertThat(str, allOf(startsWith(start), endsWith(end)));
}

10. Ein benutzerdefinierter Matcher

Wir können unseren eigenen Matcher definieren, indem wirTypeSafeMatcher erweitern. In diesem Abschnitt erstellen wir einen benutzerdefinierten Matcher, mit dem ein Test nur bestanden werden kann, wenn das Ziel eine positive Ganzzahl ist.

public class IsPositiveInteger extends TypeSafeMatcher {

    public void describeTo(Description description) {
        description.appendText("a positive integer");
    }

    @Factory
    public static Matcher isAPositiveInteger() {
        return new IsPositiveInteger();
    }

    @Override
    protected boolean matchesSafely(Integer integer) {
        return integer > 0;
    }

}

Wir müssen nur die MethodematchSafely implementieren, die überprüft, ob das Ziel tatsächlich eine positive Ganzzahl ist, und die MethodedescribeTo, die eine Fehlermeldung erzeugt, falls der Test nicht bestanden wird.

Hier ist ein Test, der unseren neuen benutzerdefinierten Matcher verwendet:

@Test
public void givenInteger_whenAPositiveValue_thenCorrect() {
    int num = 1;
    assertThat(num, isAPositiveInteger());
}

und hier ist eine Fehlermeldung, die wir erhalten, da wir eine nicht positive Ganzzahl übergeben haben:

java.lang.AssertionError: Expected: a positive integer but: was <-1>

11. Fazit

In diesem Tutorial haben wirexplored the Hamcrest API und gelernt, wie wir damit bessere und wartbarere Komponententests schreiben können.

Die vollständige Implementierung all dieser Beispiele und Codefragmentecan be found in my Hamcrest github project.