Usando correspondências de números Hamcrest

Usando correspondências de números Hamcrest

1. Visão geral

Hamcrest provides static matchers to help make unit test assertions simpler and more legible. Você pode começar a explorar alguns dos matchers disponíveishere.

Neste artigo, vamos nos aprofundar nas correspondências relacionadas a números.

2. Configuração

Para obter o Hamcrest, só precisamos adicionar a seguinte dependência Maven ao nossopom.xml:


    org.hamcrest
    java-hamcrest
    2.0.0.0

A última versão do Hamcrest pode ser encontrada emMaven Central.

3. Proximity Matchers

O primeiro conjunto de matchers que vamos dar uma olhada são aqueles quecheck if some element is close to a value +/- an error.

Mais formalmente:

value - error <= element <= value + error

Se a comparação acima for verdadeira, a asserção será aprovada.

Vamos ver em ação!

3.1. isClose com valoresDouble

Digamos que temos um número armazenado em uma variável dupla chamadaactual. E queremos testar seactual está perto de 1 +/- 0,5.

Isso é:

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

Agora vamos criar um teste de unidade usando o matcherisClose:

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

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

Como 1.3 está entre 0,5 e 1,5, o teste será aprovado. Da mesma forma, podemos testar o cenário negativo:

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

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

Agora, vamos dar uma olhada em uma situação semelhante com um tipo diferente de variáveis.

3.2. isClose com valoresBigDecimal

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

Observe quethe is matcher only decorates other matchers without adding extra logic. Isso apenas torna toda a afirmação mais legível.

Isso é tudo para os combinadores de proximidade. A seguir, vamos dar uma olhada nos combinadores de pedidos.

4. Matchers de pedidos

Como o nome diz,these matchers help make assertions regarding the order.

Existem cinco deles:

  • comparesEqualTo

  • Maior que

  • Melhor que ou igual a

  • Menor que

  • menos que ou igual a

Eles são bastante autoexplicativos, mas vamos ver alguns exemplos.

4.1. Ordem Matchers comInteger Values

O cenário mais comum seriausing these matchers with numbers.

Então, vamos prosseguir e criar alguns testes:

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

Faz sentido, certo? Observe como é simples entender o que os predicados estão afirmando.

4.2. Combinadores de pedidos com valoresString

Mesmo que comparar números faça sentido, muitas vezes é útil comparar outros tipos de elementos. É por isso queorder matchers can be applied to any class that implements the Comparable interface.

Vamos ver alguns exemplos comStrings:

@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 implementa a ordem alfabética no métodocompareTo da interfaceComparable.

Portanto, faz sentido que a palavra "Amanda" venha antes da palavra "Benjamin".

4.3. Combinadores de pedidos com valoresLocalDate

Da mesma forma que comStrings, podemos comparar as datas. Vamos dar uma olhada nos mesmos exemplos que criamos acima, mas usando objetosLocalDate:

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

É muito bom ver que a declaraçãoassertThat(today, is(lessThan(tomorrow))) é próxima ao inglês normal.

4.4. Combinadores de pedidos com classe personalizadaes

Então, por que não criar nossa própria classe e implementarComparable? Dessa forma,we can leverage order matchers to be used with custom order rules.

Vamos começar criando um beanPerson:

public class Person {
    String name;
    int age;

    // standard constructor, getters and setters
}

Agora, vamos implementarComparable:

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

Nossa implementação decompareTo compara duas pessoas por idade. Vamos agora criar alguns novos testes:

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

Matchers agora funcionarão com base em nossa lógicacompareTo.

5. NaN Matcher

Hamcrest forneceone 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. Conclusões

Como você pode ver,number matchers are very useful to simplify common assertions.

Além disso, os combinadores de Hamcrest em geral sãoself-explanatory and easy to read.

Tudo isso, além da capacidade de combinar matchers com lógica de comparação personalizada, os tornam uma ferramenta poderosa para a maioria dos projetos existentes.

A implementação completa dos exemplos deste artigo pode ser encontradaover on GitHub.