Использование Hamcrest Number Matchers

Использование Hamcrest Number Matchers

1. обзор

Hamcrest provides static matchers to help make unit test assertions simpler and more legible. Вы можете приступить к изучению некоторых доступных сопоставителейhere.

В этой статье мы подробнее рассмотрим сопоставления, связанные с числами.

2. Настроить

Чтобы получить Hamcrest, нам просто нужно добавить следующую зависимость Maven к нашемуpom.xml:


    org.hamcrest
    java-hamcrest
    2.0.0.0

Последнюю версию Hamcrest можно найти наMaven Central.

3. Бесконтактные сопоставители

Первый набор сопоставителей, который мы собираемся рассмотреть, - это те, которыеcheck if some element is close to a value +/- an error.

Более формально:

value - error <= element <= value + error

Если приведенное выше сравнение верно, утверждение пройдет.

Давайте посмотрим на это в действии!

3.1. isClose со значениямиDouble

Допустим, у нас есть число, хранящееся в переменной типа double с именемactual.. И мы хотим проверить, близко лиactual к 1 +/- 0,5.

То есть:

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

Теперь давайте создадим модульный тест, используя сопоставительisClose:

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

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

Поскольку 1,3 находится между 0,5 и 1,5, тест пройден. Таким же образом мы можем проверить негативный сценарий:

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

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

Теперь давайте посмотрим на похожую ситуацию с другим типом переменных.

3.2. isClose со значениямиBigDecimal

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

Обратите внимание, чтоthe is matcher only decorates other matchers without adding extra logic. Это делает все утверждение более читабельным.

Вот и все, что касается датчиков близости. Далее мы рассмотрим сопоставители заказов.

4. Сопоставители заказов

Как следует из их названия,these matchers help make assertions regarding the order.

Их пять:

  • comparesEqualTo

  • больше чем

  • больше или равно

  • меньше, чем

  • меньше или равно

Они в значительной степени говорят сами за себя, но давайте рассмотрим несколько примеров.

4.1. Матчеры заказов сInteger Values

Наиболее распространенный сценарий -using these matchers with numbers.

Итак, давайте продолжим и создадим несколько тестов:

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

Имеет смысл, верно? Обратите внимание, насколько просто понять, что утверждают предикаты.

4.2. Сопоставители заказов со значениямиString

Несмотря на то, что сравнение чисел имеет смысл, во многих случаях полезно сравнивать другие типы элементов. Вот почемуorder matchers can be applied to any class that implements the Comparable interface.

Давайте посмотрим на несколько примеров сStrings:

@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 реализует алфавитный порядок в методеcompareTo из интерфейсаComparable.

Таким образом, имеет смысл, что слово «Аманда» предшествует слову «Вениамин».

4.3. Сопоставители заказов со значениямиLocalDate

Так же, как сStrings, мы можем сравнивать даты. Давайте посмотрим на те же примеры, которые мы создали выше, но с использованием объектовLocalDate:

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

Очень приятно видеть, что выражениеassertThat(today, is(lessThan(tomorrow))) близко к обычному английскому.

4.4. Сопоставители заказов с настраиваемым классомes

Так почему бы не создать наш собственный класс и не реализоватьComparable? Таким образом,we can leverage order matchers to be used with custom order rules.

Начнем с создания bean-компонентаPerson:

public class Person {
    String name;
    int age;

    // standard constructor, getters and setters
}

Теперь давайте реализуемComparable:

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

Наша реализацияcompareTo сравнивает двух человек по возрасту. А теперь давайте создадим пару новых тестов:

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

Сопоставители теперь будут работать на основе нашей логикиcompareTo.

5. NaN Matcher

Hamcrest обеспечиваетone 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. Выводы

Как видите,number matchers are very useful to simplify common assertions.

Более того, сопоставители Hamcrest в целом составляютself-explanatory and easy to read.

Все это, а также возможность комбинировать сопоставления с пользовательской логикой сравнения, делают их мощным инструментом для большинства проектов.

Полную реализацию примеров из этой статьи можно найтиover on GitHub.