Тестирование с Hamcrest

Тестирование с Hamcrest

1. обзор

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

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

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

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


    org.hamcrest
    hamcrest-all
    1.3

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

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

Hamcrest обычно используется сjunit и другими средами тестирования для создания утверждений. В частности, вместо использования многочисленных методовassertjunit, мы используем только один оператор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. МатчерObject

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

Чтобы утверждать, что методtoString дляObject возвращает указанныйString:

@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. The Bean Matcher

Мы можем использовать сопоставление BeanHamcrest для проверки свойств 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("example", 25);
    assertThat(person, hasProperty("name"));
}

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

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

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

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

6. МатчерCollection

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

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

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

Чтобы проверить размерCollection:

@Test
public void givenAList_whenChecksSize_thenCorrect() {
    List 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));
}

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

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

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

@Test
public void givenAListAndValues_whenChecksListForGivenValuesWithOrder_thenCorrect() {
    List 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"));
}

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

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

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

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

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

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

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

7. МатчерNumber

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

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

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

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

@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. Text Matcher

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

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

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

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

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

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

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

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

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

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

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

9. Базовое API

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

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

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

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

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

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

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

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

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

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

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

Убедитесь, что дваObjects относятся к одному и тому же экземпляру:

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

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

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

Проверьте, все ли членыCollection удовлетворяют условию:

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

Убедитесь, чтоString неnull:

@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. Пользовательский Matcher

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

public class IsPositiveInteger extends TypeSafeMatcher {

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

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

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

}

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

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

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

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

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

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

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

Полная реализация всех этих примеров и фрагментов кодаcan be found in my Hamcrest github project.