Hamcrest Custom Matchers

Hamcrest Custom Matchers

1. Вступление

А также встроенные сопоставителиHamcrest also provides support for creating custom matchers.

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

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

Чтобы получить Hamcrest, нам нужноadd the following Maven dependency to our pom.xml:


    org.hamcrest
    java-hamcrest
    2.0.0.0
    test

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

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

Прежде чем приступить к нашим примерам,it’s important to understand the class TypeSafeMatcher.We’ll have to extend this class to create a matcher of our own.

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

  • matchesSafely(T t): содержит нашу логику сопоставления

  • describeTo(Description description): настраивает сообщение, которое получит клиент, если наша логика сопоставления не выполняется

Как мы можем видеть в первом методе,TypeSafeMatcher is parametrized, so we’ll have to declare a type when we use it. Это будет тип объекта, который мы тестируем.

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

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

Для нашего первого варианта использованияwe’ll create a matcher that returns true if a certain String contains only digits.

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

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

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

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

public class IsOnlyDigits extends TypeSafeMatcher {

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

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

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

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

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

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

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

We only need one more thing to complete our matcher: a static method to access it, поэтому он ведет себя как остальные встроенные сопоставители.

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

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

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

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

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

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

    assertThat(digits, onlyDigits());
}

Вот и все. Этот тест будет пройден, потому что вводString содержит только цифры. Помните, что для большей разборчивостиwe can use the matcher is that acts as a wrapper over any other matcher:

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

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

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

This is where we see the text that we appended to the describeTo method. Как мы могли заметить,it’s important to create a proper description of what’s expected in the test.

5. divisibleByс

Итак, что, если мы хотим создать средство сопоставления, которое определяет, делится ли число на другое число? Для этого сценарияwe’ll have to store one of the parameters somewhere.

Посмотрим, как мы можем это сделать:

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

Достаточно просто,we just added a new attribute to our class and assigned it during construction. Затем мы просто передали его в качестве параметра нашему статическому методу:

@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 also gives support to create custom matchers to be used under specific scenarios - как мы здесь исследовали. Их легко создать, и они используются точно так же, как те, что включены в библиотеку.

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