«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): содержит нашу логику сопоставления. не выполнено
Как видно из первого метода, 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 с помощью класс Строка.
Теперь мы готовы добавить нашу реализацию:
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. Использование Matcher
Чтобы использовать наш новый matcher, мы создадим тест:
@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. divisibleBy
Итак, что если мы хотим создать сопоставитель, определяющий, делится ли число на другое число? Для этого сценария нам нужно где-то сохранить один из параметров.
Давайте посмотрим, как мы можем это сделать:
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.