Utilisation des correspondeurs de nombres Hamcrest

Utilisation des correspondeurs de nombres Hamcrest

1. Vue d'ensemble

Hamcrest provides static matchers to help make unit test assertions simpler and more legible. Vous pouvez commencer à explorer certains des correspondants disponibleshere.

Dans cet article, nous allons approfondir les correspondances liées aux nombres.

2. Installer

Pour obtenir Hamcrest, il suffit d'ajouter la dépendance Maven suivante à nospom.xml:


    org.hamcrest
    java-hamcrest
    2.0.0.0

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

3. Matchers de proximité

Le premier ensemble de correspondances que nous allons examiner sont ceux quicheck if some element is close to a value +/- an error.

Plus formellement:

value - error <= element <= value + error

Si la comparaison ci-dessus est vraie, l'assertion passera.

Voyons cela en action!

3.1. isClose avec les valeursDouble

Disons que nous avons un nombre stocké dans une variable double appeléeactual. Et, nous voulons tester siactual est proche de 1 +/- 0,5.

C'est:

1 - 0.5 <= actual <= 1 + 0.5
    0.5 <= actual <= 1.5

Créons maintenant un test unitaire à l’aide du matcherisClose:

@Test
public void givenADouble_whenCloseTo_thenCorrect() {
    double actual = 1.3;
    double operand = 1;
    double error = 0.5;

    assertThat(actual, closeTo(operand, error));
}

Comme 1.3 est compris entre 0.5 et 1.5, le test réussira. De la même manière, nous pouvons tester le scénario négatif:

@Test
public void givenADouble_whenNotCloseTo_thenCorrect() {
    double actual = 1.6;
    double operand = 1;
    double error = 0.5;

    assertThat(actual, not(closeTo(operand, error)));
}

Examinons maintenant une situation similaire avec un type de variable différent.

3.2. isClose avec les valeursBigDecimal

isClose is overloaded and can be used same as with double values, but with BigDecimal objects:

@Test
public void givenABigDecimal_whenCloseTo_thenCorrect() {
    BigDecimal actual = new BigDecimal("1.0003");
    BigDecimal operand = new BigDecimal("1");
    BigDecimal error = new BigDecimal("0.0005");

    assertThat(actual, is(closeTo(operand, error)));
}

@Test
public void givenABigDecimal_whenNotCloseTo_thenCorrect() {
    BigDecimal actual = new BigDecimal("1.0006");
    BigDecimal operand = new BigDecimal("1");
    BigDecimal error = new BigDecimal("0.0005");

    assertThat(actual, is(not(closeTo(operand, error))));
}

Veuillez noter quethe is matcher only decorates other matchers without adding extra logic. Cela rend simplement l'assertion plus lisible.

C’est à peu près tout pour les correspondants de proximité. Ensuite, nous examinerons les correspondances de commandes.

4. Correspondants d'ordre

Comme leur nom l'indique,these matchers help make assertions regarding the order.

Il y en a cinq:

  • compareEqualTo

  • plus grand que

  • Plus grand ou égal à

  • moins que

  • inférieur ou égal à

Ils sont assez explicites, mais voyons quelques exemples.

4.1. Correspondants de commande avecInteger Values

Le scénario le plus courant seraitusing these matchers with numbers.

Alors, allons-y et créons quelques tests:

@Test
public void given5_whenComparesEqualTo5_thenCorrect() {
    Integer five = 5;

    assertThat(five, comparesEqualTo(five));
}

@Test
public void given5_whenNotComparesEqualTo7_thenCorrect() {
    Integer seven = 7;
    Integer five = 5;

    assertThat(five, not(comparesEqualTo(seven)));
}

@Test
public void given7_whenGreaterThan5_thenCorrect() {
    Integer seven = 7;
    Integer five = 5;

    assertThat(seven, is(greaterThan(five)));
}

@Test
public void given7_whenGreaterThanOrEqualTo5_thenCorrect() {
    Integer seven = 7;
    Integer five = 5;

    assertThat(seven, is(greaterThanOrEqualTo(five)));
}

@Test
public void given5_whenGreaterThanOrEqualTo5_thenCorrect() {
    Integer five = 5;

    assertThat(five, is(greaterThanOrEqualTo(five)));
}

@Test
public void given3_whenLessThan5_thenCorrect() {
   Integer three = 3;
   Integer five = 5;

   assertThat(three, is(lessThan(five)));
}

@Test
public void given3_whenLessThanOrEqualTo5_thenCorrect() {
   Integer three = 3;
   Integer five = 5;

   assertThat(three, is(lessThanOrEqualTo(five)));
}

@Test
public void given5_whenLessThanOrEqualTo5_thenCorrect() {
   Integer five = 5;

   assertThat(five, is(lessThanOrEqualTo(five)));
}

C'est logique, non? Veuillez noter à quel point il est simple de comprendre ce que les prédicats affirment.

4.2. Correspondants d'ordre avec des valeursString

Même si la comparaison de nombres a tout son sens, il est souvent utile de comparer d’autres types d’éléments. C’est pourquoiorder matchers can be applied to any class that implements the Comparable interface.

Voyons quelques exemples avecStrings:

@Test
public void givenBenjamin_whenGreaterThanAmanda_thenCorrect() {
    String amanda = "Amanda";
    String benjamin = "Benjamin";

    assertThat(benjamin, is(greaterThan(amanda)));
}

@Test
public void givenAmanda_whenLessThanBenajmin_thenCorrect() {
    String amanda = "Amanda";
    String benjamin = "Benjamin";

    assertThat(amanda, is(lessThan(benjamin)));
}

String implémente l'ordre alphabétique dans la méthodecompareTo à partir de l'interfaceComparable.

Il est donc logique que le mot «Amanda» soit placé avant le mot «Benjamin».

4.3. Correspondants d'ordre avec des valeursLocalDate

Comme pourStrings, nous pouvons comparer les dates. Jetons un coup d'œil aux mêmes exemples que nous avons créés ci-dessus mais en utilisant les objetsLocalDate:

@Test
public void givenToday_whenGreaterThanYesterday_thenCorrect() {
    LocalDate today = LocalDate.now();
    LocalDate yesterday = today.minusDays(1);

    assertThat(today, is(greaterThan(yesterday)));
}

@Test
public void givenToday_whenLessThanTomorrow_thenCorrect() {
    LocalDate today = LocalDate.now();
    LocalDate tomorrow = today.plusDays(1);

    assertThat(today, is(lessThan(tomorrow)));
}

C’est très agréable de voir que l’instructionassertThat(today, is(lessThan(tomorrow))) est proche de l’anglais ordinaire.

4.4. Correspondants de commandes avec classe personnaliséees

Alors, pourquoi ne pas créer notre propre classe et implémenterComparable? De cette façon,we can leverage order matchers to be used with custom order rules.

Commençons par créer un beanPerson:

public class Person {
    String name;
    int age;

    // standard constructor, getters and setters
}

Maintenant, implémentonsComparable:

public class Person implements Comparable {

    // ...

    @Override
    public int compareTo(Person o) {
        if (this.age == o.getAge()) return 0;
        if (this.age > o.getAge()) return 1;
        else return -1;
    }
}

Notre implémentationcompareTo compare deux personnes par leur âge. Créons maintenant quelques nouveaux tests:

@Test
public void givenAmanda_whenOlderThanBenjamin_thenCorrect() {
    Person amanda = new Person("Amanda", 20);
    Person benjamin = new Person("Benjamin", 18);

    assertThat(amanda, is(greaterThan(benjamin)));
}

@Test
public void
givenBenjamin_whenYoungerThanAmanda_thenCorrect() {
    Person amanda = new Person("Amanda", 20);
    Person benjamin = new Person("Benjamin", 18);

    assertThat(benjamin, is(lessThan(amanda)));
}

Les correspondants fonctionneront désormais selon notre logiquecompareTo.

5. Matcher NaN

Hamcrest fournitone extra number matcher to define if a number is actually, not a number:

@Test
public void givenNaN_whenIsNotANumber_thenCorrect() {
    double zero = 0d;

    assertThat(zero / zero, is(notANumber()));
}

6. Conclusions

Comme vous pouvez le voir,number matchers are very useful to simplify common assertions.

De plus, les correspondants Hamcrest en général sontself-explanatory and easy to read.

Tout cela, en plus de la possibilité de combiner des corrélateurs avec une logique de comparaison personnalisée, en fait un outil puissant pour la plupart des projets.

L'implémentation complète des exemples de cet article peut être trouvéeover on GitHub.