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

Тестирование с помощью Hamcrest

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

1. Обзор

Hamcrest — это хорошо известная платформа, используемая для модульного тестирования в экосистеме Java. Он встроен в JUnit и, проще говоря, использует существующие предикаты, называемые классами сопоставления, для создания утверждений.

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

2. Установка подколенного гребня

Мы можем использовать Hamcrest с maven, добавив следующую зависимость в наш файл pom.xml :

<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<version>1.3</version>
</dependency>

Последнюю версию этой библиотеки всегда можно найти здесь .

3. Пример теста

Hamcrest обычно используется с junit и другими средами тестирования для создания утверждений. В частности, вместо использования многочисленных методов assert junit мы используем только один оператор assertThat API с соответствующими сопоставлениями.

Давайте рассмотрим пример, который проверяет два String на равенство независимо от регистра. Это должно дать нам четкое представление о том, как Hamcrest вписывается в метод тестирования:

public class StringMatcherTest {

@Test
public void given2Strings_whenEqual_thenCorrect() {
String a = "foo";
String b = "FOO";
assertThat(a, equalToIgnoringCase(b));
}
}

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

4. Сопоставитель объектов

Hamcrest предоставляет средства сопоставления для создания утверждений для произвольных объектов Java.

Чтобы утверждать, что метод toString объекта возвращает указанную строку :

@Test
public void givenBean_whenToStringReturnsRequiredString_thenCorrect(){
Person person=new Person("Barrack", "Washington");
String str=person.toString();
assertThat(person,hasToString(str));
}

Мы также можем проверить, что один класс является подклассом другого:

@Test
public void given2Classes_whenOneInheritsFromOther_thenCorrect(){
assertThat(Cat.class,typeCompatibleWith(Animal.class));
}
}

5. Сопоставитель фасоли

Мы можем использовать сопоставитель Bean от Hamcrest для проверки свойств Java-бина.

Предположим, что используется следующий bean-компонент Person :

public class Person {
String name;
String address;

public Person(String personName, String personAddress) {
name = personName;
address = personAddress;
}
}

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

@Test
public void givenBean_whenHasValue_thenCorrect() {
Person person = new Person("ForEach", 25);
assertThat(person, hasProperty("name"));
}

Мы также можем проверить, есть ли у Person свойство адреса , инициализированное Нью-Йорком:

@Test
public void givenBean_whenHasCorrectValue_thenCorrect() {
Person person = new Person("ForEach", "New York");
assertThat(person, hasProperty("address", equalTo("New York")));
}

Мы также можем проверить, построены ли два объекта Person с одинаковыми значениями:

@Test
public void given2Beans_whenHavingSameValues_thenCorrect() {
Person person1 = new Person("ForEach", "New York");
Person person2 = new Person("ForEach", "New York");
assertThat(person1, samePropertyValuesAs(person2));
}

6. Сопоставитель коллекций

Hamcrest предоставляет средства сопоставления для проверки Collection s.

Простая проверка, чтобы узнать, пуста ли коллекция :

@Test
public void givenCollection_whenEmpty_thenCorrect() {
List<String> emptyList = new ArrayList<>();
assertThat(emptyList, empty());
}

Чтобы проверить размер коллекции:

@Test
public void givenAList_whenChecksSize_thenCorrect() {
List<String> hamcrestMatchers = Arrays.asList(
"collections", "beans", "text", "number");
assertThat(hamcrestMatchers, hasSize(4));
}

Мы также можем использовать его, чтобы утверждать, что массив имеет требуемый размер:

@Test
public void givenArray_whenChecksSize_thenCorrect() {
String[] hamcrestMatchers = { "collections", "beans", "text", "number" };
assertThat(hamcrestMatchers, arrayWithSize(4));
}

Чтобы проверить, содержит ли коллекция заданные элементы, независимо от порядка:

@Test
public void givenAListAndValues_whenChecksListForGivenValues_thenCorrect() {
List<String> hamcrestMatchers = Arrays.asList(
"collections", "beans", "text", "number");
assertThat(hamcrestMatchers,
containsInAnyOrder("beans", "text", "collections", "number"));
}

Чтобы далее утверждать, что члены коллекции находятся в заданном порядке:

@Test
public void givenAListAndValues_whenChecksListForGivenValuesWithOrder_thenCorrect() {
List<String> hamcrestMatchers = Arrays.asList(
"collections", "beans", "text", "number");
assertThat(hamcrestMatchers,
contains("collections", "beans", "text", "number"));
}

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

@Test
public void givenArrayAndValue_whenValueFoundInArray_thenCorrect() {
String[] hamcrestMatchers = { "collections", "beans", "text", "number" };
assertThat(hamcrestMatchers, hasItemInArray("text"));
}

Мы также можем использовать альтернативный сопоставитель для того же теста:

@Test
public void givenValueAndArray_whenValueIsOneOfArrayElements_thenCorrect() {
String[] hamcrestMatchers = { "collections", "beans", "text", "number" };
assertThat("text", isOneOf(hamcrestMatchers));
}

Или мы можем сделать то же самое с другим сопоставителем, например так:

@Test
public void givenValueAndArray_whenValueFoundInArray_thenCorrect() {
String[] array = new String[] { "collections", "beans", "text",
"number" };
assertThat("beans", isIn(array));
}

Мы также можем проверить, содержит ли массив заданные элементы независимо от порядка:

@Test
public void givenArrayAndValues_whenValuesFoundInArray_thenCorrect() {
String[] hamcrestMatchers = { "collections", "beans", "text", "number" };
assertThat(hamcrestMatchers,
arrayContainingInAnyOrder("beans", "collections", "number",
"text"));
}

Чтобы проверить, содержит ли массив заданные элементы, но в заданном порядке:

@Test
public void givenArrayAndValues_whenValuesFoundInArrayInOrder_thenCorrect() {
String[] hamcrestMatchers = { "collections", "beans", "text", "number" };
assertThat(hamcrestMatchers,
arrayContaining("collections", "beans", "text", "number"));
}

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

Чтобы проверить, содержит ли он заданный ключ:

@Test
public void givenMapAndKey_whenKeyFoundInMap_thenCorrect() {
Map<String, String> map = new HashMap<>();
map.put("blogname", "foreach");
assertThat(map, hasKey("blogname"));
}

и заданное значение:

@Test
public void givenMapAndValue_whenValueFoundInMap_thenCorrect() {
Map<String, String> map = new HashMap<>();
map.put("blogname", "foreach");
assertThat(map, hasValue("foreach"));
}

и, наконец, заданная запись (ключ, значение):

@Test
public void givenMapAndEntry_whenEntryFoundInMap_thenCorrect() {
Map<String, String> map = new HashMap<>();
map.put("blogname", "foreach");
assertThat(map, hasEntry("blogname", "foreach"));
}

7. Сопоставитель чисел

Сопоставители чисел используются для выполнения утверждений над переменными класса Number .

Чтобы проверить условие «больше чем» :

@Test
public void givenAnInteger_whenGreaterThan0_thenCorrect() {
assertThat(1, greaterThan(0));
}

Чтобы проверить условие «больше чем» или «равно» :

@Test
public void givenAnInteger_whenGreaterThanOrEqTo5_thenCorrect() {
assertThat(5, greaterThanOrEqualTo(5));
}

Чтобы проверить условие lessThan :

@Test
public void givenAnInteger_whenLessThan0_thenCorrect() {
assertThat(-1, lessThan(0));
}

Чтобы проверить условие lessThan или equalTo :

@Test
public void givenAnInteger_whenLessThanOrEqTo5_thenCorrect() {
assertThat(-1, lessThanOrEqualTo(5));
}

Чтобы проверить условие closeTo :

@Test
public void givenADouble_whenCloseTo_thenCorrect() {
assertThat(1.2, closeTo(1, 0.5));
}

Давайте обратим пристальное внимание на последний сопоставитель, closeTo. Первый аргумент, операнд, — это тот, с которым сравнивается цель, а второй аргумент — это допустимое отклонение от операнда . Это означает, что если целью является операнд+отклонение или операнд-отклонение, то тест будет пройден.

8. Сопоставитель текста

Утверждение для String стало проще, аккуратнее и интуитивно понятнее благодаря сопоставителям текста Hamcrest . Мы рассмотрим их в этом разделе.

Чтобы проверить, пуста ли строка :

@Test
public void givenString_whenEmpty_thenCorrect() {
String str = "";
assertThat(str, isEmptyString());
}

Чтобы проверить, является ли строка пустой или нулевой :

@Test
public void givenString_whenEmptyOrNull_thenCorrect() {
String str = null;
assertThat(str, isEmptyOrNullString());
}

Чтобы проверить равенство двух String s, игнорируя пробелы:

@Test
public void given2Strings_whenEqualRegardlessWhiteSpace_thenCorrect() {
String str1 = "text";
String str2 = " text ";
assertThat(str1, equalToIgnoringWhiteSpace(str2));
}

Мы также можем проверить наличие одной или нескольких подстрок в данной строке в заданном порядке:

@Test
public void givenString_whenContainsGivenSubstring_thenCorrect() {
String str = "calligraphy";
assertThat(str, stringContainsInOrder(Arrays.asList("call", "graph")));
}

Наконец, мы можем проверить равенство двух String независимо от регистра:

@Test
public void given2Strings_whenEqual_thenCorrect() {
String a = "foo";
String b = "FOO";
assertThat(a, equalToIgnoringCase(b));
}

9. Основной API

Основной API Hamcrest должен использоваться сторонними поставщиками фреймворков. Тем не менее, он предлагает нам несколько отличных конструкций, чтобы сделать наши модульные тесты более читабельными, а также некоторые основные средства сопоставления, которые можно использовать так же легко.

Читаемость с конструкцией is на сопоставителе:

@Test
public void given2Strings_whenIsEqualRegardlessWhiteSpace_thenCorrect() {
String str1 = "text";
String str2 = " text ";
assertThat(str1, is(equalToIgnoringWhiteSpace(str2)));
}

Это конструкция для простого типа данных:

@Test
public void given2Strings_whenIsEqual_thenCorrect() {
String str1 = "text";
String str2 = "text";
assertThat(str1, is(str2));
}

Отрицание с конструкцией not на сопоставителе:

@Test
public void given2Strings_whenIsNotEqualRegardlessWhiteSpace_thenCorrect() {
String str1 = "text";
String str2 = " texts ";
assertThat(str1, not(equalToIgnoringWhiteSpace(str2)));
}

Конструкция not для простого типа данных:

@Test
public void given2Strings_whenNotEqual_thenCorrect() {
String str1 = "text";
String str2 = "texts";
assertThat(str1, not(str2));
}

Проверьте, содержит ли строка заданную подстроку:

@Test
public void givenAStrings_whenContainsAnotherGivenString_thenCorrect() {
String str1 = "calligraphy";
String str2 = "call";
assertThat(str1, containsString(str2));
}

Проверьте, начинается ли строка с заданной подстроки:

@Test
public void givenAString_whenStartsWithAnotherGivenString_thenCorrect() {
String str1 = "calligraphy";
String str2 = "call";
assertThat(str1, startsWith(str2));
}

Проверьте, заканчивается ли строка заданной подстрокой:

@Test
public void givenAString_whenEndsWithAnotherGivenString_thenCorrect() {
String str1 = "calligraphy";
String str2 = "phy";
assertThat(str1, endsWith(str2));
}

Проверьте, относятся ли два Object к одному и тому же экземпляру:

@Test
public void given2Objects_whenSameInstance_thenCorrect() {
Cat cat=new Cat();
assertThat(cat, sameInstance(cat));
}

Проверьте, является ли объект экземпляром данного класса:

@Test
public void givenAnObject_whenInstanceOfGivenClass_thenCorrect() {
Cat cat=new Cat();
assertThat(cat, instanceOf(Cat.class));
}

Проверьте, соответствуют ли все члены коллекции условию:

@Test
public void givenList_whenEachElementGreaterThan0_thenCorrect() {
List<Integer> list = Arrays.asList(1, 2, 3);
int baseCase = 0;
assertThat(list, everyItem(greaterThan(baseCase)));
}

Убедитесь, что строка не является нулевой :

@Test
public void givenString_whenNotNull_thenCorrect() {
String str = "notnull";
assertThat(str, notNullValue());
}

Цепочка условий вместе, тест проходит, когда цель соответствует любому из условий, подобно логическому ИЛИ:

@Test
public void givenString_whenMeetsAnyOfGivenConditions_thenCorrect() {
String str = "calligraphy";
String start = "call";
String end = "foo";
assertThat(str, anyOf(startsWith(start), containsString(end)));
}

Соедините условия вместе, тест проходит только тогда, когда цель соответствует всем условиям, аналогично логическому И:

@Test
public void givenString_whenMeetsAllOfGivenConditions_thenCorrect() {
String str = "calligraphy";
String start = "call";
String end = "phy";
assertThat(str, allOf(startsWith(start), endsWith(end)));
}

10. Пользовательский матчер

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

public class IsPositiveInteger extends TypeSafeMatcher<Integer> {

public void describeTo(Description description) {
description.appendText("a positive integer");
}

@Factory
public static Matcher<Integer> isAPositiveInteger() {
return new IsPositiveInteger();
}

@Override
protected boolean matchesSafely(Integer integer) {
return integer > 0;
}

}

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

Вот тест, в котором используется наш новый пользовательский сопоставитель:

@Test
public void givenInteger_whenAPositiveValue_thenCorrect() {
int num = 1;
assertThat(num, isAPositiveInteger());
}

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

java.lang.AssertionError: Expected: a positive integer but: was <-1>

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

В этом руководстве мы изучили Hamcrest API и узнали, как с его помощью писать более качественные и удобные в сопровождении модульные тесты.

Полную реализацию всех этих примеров и фрагментов кода можно найти в моем проекте Hamcrest на github .