«1. Обзор

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

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

2. Настройка Hamcrest

Мы можем использовать 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 с соответствующими сопоставителями.

Давайте рассмотрим пример, который проверяет две строки на равенство независимо от регистра. Это должно дать нам четкое представление о том, как 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:

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

public class Person {
    String name;
    String address;

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

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

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

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

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

6. Средство сопоставления коллекций

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

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

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

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

@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", "baeldung");
    assertThat(map, hasKey("blogname"));
}

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

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

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

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

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

Проверка условия \»больше чем\»:

Проверка условия \»больше чем\» или \»равно\»:

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

Проверка условия \»меньше чем\»:

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

Проверка условия \»меньше\» или \»равно\»:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

9. Основной API

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

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

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

Конструкция is на простом типе данных:

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

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

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

построить на простом типе данных:

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

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

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

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

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

10. Пользовательский сопоставление

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

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

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

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

}

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

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

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

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

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

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

«

The full implementation of all these examples and code snippets can be found in my Hamcrest github project.