Перейти к основному содержимому

Пользовательские сопоставители Hamcrest

· 4 мин. чтения

1. Введение

Помимо встроенных сопоставителей, Hamcrest также поддерживает создание пользовательских сопоставителей.

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

2. Настройка пользовательских сопоставлений

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

<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>java-hamcrest</artifactId>
<version>2.0.0.0</version>
<scope>test</scope>
</dependency>

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

3. Представляем TypeSafeMatcher

Прежде чем приступить к нашим примерам, важно понять класс TypeSafeMatcher . Нам придется расширить этот класс, чтобы создать собственный сопоставитель.

TypeSafeMatcher — абстрактный класс, поэтому все подклассы должны реализовывать следующие методы:

  • matchSafely(T t) : содержит нашу логику сопоставления
  • descriptionTo(Description description) : настраивает сообщение, которое клиент получит, когда наша логика сопоставления не будет выполнена.

Как видно из первого метода, TypeSafeMatcher параметризован, поэтому при его использовании нам придется объявлять тип. Это будет тип объекта, который мы тестируем.

Давайте проясним это, рассмотрев наш первый пример в следующем разделе.

4. Создание сопоставителя onlyDigits

Для нашего первого варианта использования мы создадим сопоставитель, который возвращает true, если определенная строка содержит только цифры.

Таким образом, onlyDigits , применяемые к «123», должны возвращать true , а « hello1 » и « bye » должны возвращать false.

Давайте начнем!

4.1. Создание сопоставления

Чтобы начать с нашего сопоставления, мы создадим класс, который расширяет TypeSafeMatcher :

public class IsOnlyDigits extends TypeSafeMatcher<String> {

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

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

Обратите внимание, что поскольку объект, который мы будем тестировать, является текстом, мы параметризуем наш подкласс TypeSafeMatcher с помощью класса String.

Теперь мы готовы добавить нашу реализацию:

public class IsOnlyDigits extends TypeSafeMatcher<String> {

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

Как мы видим, matchSafey пытается преобразовать нашу входную строку в целое число . В случае успеха возвращает true . В случае неудачи возвращается false . Он успешно отвечает нашему варианту использования.

С другой стороны, descriptionTo прикрепляет текст, который представляет наши ожидания. Мы увидим, как это будет выглядеть дальше, когда мы будем использовать наш сопоставитель.

Нам нужно только еще одно, чтобы завершить наш сопоставитель: статический метод для доступа к нему , чтобы он вел себя как остальные встроенные сопоставители.

Итак, мы добавим что-то вроде этого:

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

И мы закончили! Давайте посмотрим, как использовать этот сопоставитель в следующем разделе.

4.2. Использование сопоставления

Чтобы использовать наш новый сопоставитель, мы создадим тест :

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

assertThat(digits, onlyDigits());
}

Вот и все. Этот тест пройдет, потому что входная строка содержит только цифры. Помните, что для большей разборчивости мы можем использовать сопоставитель is , который действует как обертка над любым другим сопоставителем :

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

Наконец, если мы запустим тот же тест, но со входом «123ABC», выходное сообщение будет таким:

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

Здесь мы видим текст, который мы добавили к методу descriptionTo . Как мы могли заметить, важно создать правильное описание того, что ожидается в тесте.

5. делится на

Итак, что, если мы хотим создать сопоставитель, который определяет, делится ли число на другое число? Для этого сценария нам нужно где-то сохранить один из параметров.

Давайте посмотрим, как мы можем это сделать:

public class IsDivisibleBy extends TypeSafeMatcher<Integer> {

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<Integer> divisibleBy(Integer divider) {
return new IsDivisibleBy(divider);
}
}

Достаточно просто, мы просто добавили новый атрибут в наш класс и присвоили его во время построения . Затем мы просто передали его в качестве параметра нашему статическому методу:

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

Вот и все! У нас уже есть наш сопоставитель, использующий более одного входа!

6. Заключение

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

Более того, если какой-либо конкретный случай не охвачен, Hamcrest также предоставляет поддержку для создания пользовательских сопоставителей, которые будут использоваться в определенных сценариях — как мы рассмотрели здесь . Их просто создать, и они используются точно так же, как те, что включены в библиотеку.

Чтобы получить полную реализацию этих примеров, обратитесь к проекту GitHub .