Hamcrest Custom Matchers

Hamcrest Custom Matchers

1. introduction

En plus des adaptateurs intégrés,Hamcrest also provides support for creating custom matchers.

Dans ce didacticiel, nous allons examiner de plus près comment les créer et les utiliser. Pour avoir un aperçu des correspondants disponibles, reportez-vous àthis article.

2. Configuration des correspondants personnalisés

Pour obtenir Hamcrest, nous devonsadd the following Maven dependency to our pom.xml:


    org.hamcrest
    java-hamcrest
    2.0.0.0
    test

La dernière version de Hamcrest peut être trouvée surMaven Central.

3. Présentation deTypeSafeMatcher

Avant de commencer avec nos exemples,it’s important to understand the class TypeSafeMatcher.We’ll have to extend this class to create a matcher of our own.

TypeSafeMatcher est une classe abstraite, donc toutes les sous-classes doivent implémenter les méthodes suivantes:

  • matchesSafely(T t): contient notre logique de correspondance

  • describeTo(Description description): personnalise le message que le client recevra lorsque notre logique de correspondance n'est pas remplie

Comme nous pouvons le voir dans la première méthode,TypeSafeMatcher is parametrized, so we’ll have to declare a type when we use it. Ce sera le type d’objet que nous testons.

Clarifions ce point en regardant notre premier exemple dans la section suivante.

4. Création du matcheronlyDigits

Pour notre premier cas d'utilisation,we’ll create a matcher that returns true if a certain String contains only digits.

Ainsi,onlyDigits appliqué à «123» doit renvoyertrue tandis que «hello1» et «bye» doivent renvoyer false.

Commençons!

4.1. Création de matcher

Pour commencer avec notre correspondance, nous allons créer une classe qui étendTypeSafeMatcher:

public class IsOnlyDigits extends TypeSafeMatcher {

    @Override
    protected boolean matchesSafely(String s) {
        // ...
    }

    @Override
    public void describeTo(Description description) {
        // ...
    }
}

Veuillez noter que l’objet que nous allons tester étant un texte, nous paramétrons notre sous-classe deTypeSafeMatcher avec la classeString.

Nous sommes maintenant prêts à ajouter notre mise en œuvre:

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");
    }
}

Comme nous pouvons le voir,matchesSafey essaie d'analyser notre entréeString en unInteger. S'il réussit, il renvoietrue. En cas d'échec, il renvoiefalse. Il répond avec succès à notre cas d'utilisation.

De l'autre côté,describeTo joint un texte qui représente nos attentes. Nous verrons comment cela apparaîtra ensuite lorsque nous utiliserons notre outil de correspondance.

We only need one more thing to complete our matcher: a static method to access it, donc il se comporte comme le reste des correspondants intégrés.

Nous allons donc ajouter quelque chose comme ceci:

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

Et nous avons terminé! Voyons comment utiliser cette correspondance dans la section suivante.

4.2. Utilisation du matcher

Àuse our brand new matcher, we’ll create a test:

@Test
public void givenAString_whenIsOnlyDigits_thenCorrect() {
    String digits = "1234";

    assertThat(digits, onlyDigits());
}

Et c'est tout. Ce test réussira car l'entréeString ne contient que des chiffres. N'oubliez pas que, pour le rendre un peu plus lisible,we can use the matcher is that acts as a wrapper over any other matcher:

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

Enfin, si nous exécutions le même test mais avec l’entrée «123ABC», le message de sortie serait:

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

This is where we see the text that we appended to the describeTo method. Comme nous l'avons peut-être remarqué,it’s important to create a proper description of what’s expected in the test.

5. divisibleBy

Alors, si nous voulions créer un matcher qui définit si un nombre est divisible par un autre nombre? Pour ce scénario,we’ll have to store one of the parameters somewhere.

Voyons comment nous pouvons y parvenir:

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);
    }
}

Assez simple,we just added a new attribute to our class and assigned it during construction. Ensuite, nous venons de le passer en tant que paramètre à notre méthode statique:

@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))));
}

Et c'est tout! Notre matcher utilise déjà plus d'une entrée!

6. Conclusion

Hamcrest fournit des correspondants qui couvrent la plupart des cas d'utilisation auxquels un développeur doit généralement faire face lors de la création d'assertions.

De plus, si un cas spécifique n’est pas couvert,Hamcrest also gives support to create custom matchers to be used under specific scenarios - comme nous l’avons exploré ici. Ils sont simples à créer et ils sont utilisés exactement comme ceux inclus dans la bibliothèque.

Pour obtenir l'implémentation complète de ces exemples, veuillez vous référer àthe GitHub Project.