Skip to content

Examples for library matchers-java to show how to write and work with matchers

License

Notifications You must be signed in to change notification settings

yandex-qatools/matchers-examples

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

##Предположим, у нас есть набор неких фруктов.

public class Fruit {
    ...
    
  public Color getColor() {...}

  public boolean isSweet() {...}

  public Shape getShape() {...}
}

Помимо фруктов, у нас есть и конвейер, с задачей - отсеять ненужное, проделав серию тестов. И вот удача - у нас как раз под рукой оказался аппарат, который умеет определять сладкий ли фрукт, его цвет, сравнивать форму с рядом известных и делать еще множество проверок. Аппарат этот называется JUnit. Перед началом теста, на конвейер вываливается новый фрукт.

@Before
public void setUp() throws Exception {
  someFruit = getNextFruit();
}

Определим сперва, что фрукт круглый:

@Test
public void orangeIsRoundWithMatcher() {
  assertThat(someFruit, is(round()));
}

Затем, что фрукт сладкий:

@Test
public void orangeIsSweetWithMatcher() {
        assertThat(someFruit, is(sweet()));
}

И, наконец, посмотрим на его цвет.

@Test
public void orangeHasColorWithMatcher() {
  assertThat(someFruit, hasColor(Color.ORANGE));
}

Для такой красоты, существует специальная библиотека Hamcrest. Она содержит в себе и интерфейс для реализации и методы - assertThat и assumeThat (этот метод на самом деле внутри JUnit, но использует интерфейс из Hamcrest), которые и спрашивают матчер об объекте, принимая решение. Начиная с версии 4.11 - в зависимостях JUnit библиотека Hamcrest имеет версию не ниже 1.3. Именно она ввела, интерфейс, в котором реализовано все что описано дальше. Поэтому, используя мавен - достаточно подключить JUnit 4.11 - и минимально необходимый набор инструментов готов к использованию. А для полного набора всех доступных матчеров из поставки Hamcrest, понадобится артифакт hamcrest-all, который можно подключить отдельно.

Как может выглядеть ваш pom

###Как это работает? В библиотеке есть абстрактный класс TypeSafeMatcher<Fruit> - параметризуемый по типу проверяемого объекта. Класс предоставляет для переопределения три метода

public boolean matchesSafely(Fruit fruit) - логика проверки,
public void describeTo(Description description) - описание ожидаемого значения,
protected void describeMismatchSafely(Fruit item, Description mismatchDescription) - описание полученного значения.

Экземпляр класса, расширяющего этот, перед выполнением собственного кода выполнит родительский - рутинные проверки поступающего объекта на null и соответствие указанному классу. Например, матчер проверяющий форму фрукта выглядит так:

public class ShapeMatcher extends TypeSafeMatcher<Fruit> {
    private Shape expected;

    public ShapeMatcher(Shape expected) {
        this.expected = expected;
    }

    @Override
    public boolean matchesSafely(Fruit fruit) {
        return expected.equals(fruit.getShape());
    }

    @Override
    protected void describeMismatchSafely(Fruit item, Description mismatchDescription) {
        mismatchDescription.appendText("fruit has shape - ").appendValue(item.getShape());
    }

    @Override
    public void describeTo(Description description) {
        description.appendText("shape - ").appendValue(expected);
    }

    @Factory
    public static ShapeMatcher round() {
        return new ShapeMatcher(Shape.ROUND);
    }
}

Количество кода сперва пугает. Но если приглядеться, сразу заметно - каждое логическое действие выделено в отдельный метод. А в тесте вызов умещается в одно слово - использовать очень просто! Но и это не все Частая ситуация, как, например, выше - необходимость использовать для проверки только одно свойство объекта. Целый класс для этого - расточительство времени и сил. Тут на помощь приходят анонимные классы Java и абстрактный класс FeatureMatcher<WhatWeGet, WhatWeWannaCheck>, параметризуемый двумя типами:

  • какой объект поступит на вход
  • и свойство какого типа нужно проверить.

Конструктор у этого класса один и требует 3 атрибута:

  • матчер, который применим к WhatWeWannaCheck типу,
  • описание ожидания (оно добавится к описанию субматчера),
  • описание полученного значения (оно добавится к мисматч-описанию субматчера).

Потомок этого класса, переопределив метод featureValueOfпозволит вытащить нужное свойство из объекта, после чего применить к нему существующий матчер, а их в поставке Hamcrest хватает для любых стандартных типов. Перепишем наш матчер для формы, а заодно и остальные, используя этот класс:

public class Matchers {

    public static Matcher<Fruit> hasShape(final Shape shape) {
        return new FeatureMatcher<Fruit, Shape>(equalTo(shape), "fruit has shape - ", "shape -") {
            @Override
            protected Shape featureValueOf(Fruit fruit) {
                return fruit.getShape();
            }
        };
    }

    public static Matcher<Fruit> round() {
        return hasShape(Shape.ROUND);
    }


    public static Matcher<Fruit> sweet() {
        return new FeatureMatcher<Fruit, Boolean>(is(true), "fruit should be sweet", "sweet -") {
            @Override
            protected Boolean featureValueOf(Fruit fruit) {
                return fruit.isSweet();
            }
        };
    }


    public static Matcher<Fruit> hasColor(Color color) {
        return new FeatureMatcher<Fruit, Color>(equalTo(color), "fruit have color - ", "color -") {
            @Override
            protected String featureValueOf(Fruit fruit) {
                return fruit.getColor();
            }
        };
    }

}

##Feel the POWER OF MATCHERS Одним из главных преимуществ матчеров является возможность их объединения. Для таких целей в Hamcrest есть целый ряд специальных связующих - allOf, anyOf, both, either. Каждый из них заботливо соединит и описание ожидаемого значения и описание проваленных матчеров из цепочки.

@Test
public void orangeBothSweetRoundAndOrangeColorWithMatchers() throws Exception {
  assumeThat(someFruit, both(round()).and(sweet()).and(hasColor(Color.ORANGE)));
}

[Исходники всех тестов] (https://github.com/lanwen/matchers-examples/blob/master/src/test/java/ru/yandex/qatools/examples/FruitDegustationTest.java)

###Матчеры и коллекции Еще одна из замечательных возможностей - применение одного или ряда матчеров к коллекции. Предположим, вместо одного фрукта, за раз стал поступать целый набор. И все нужно проверить одновременно. Больше не нужно никаких циклов, все проще простого:

@Test
public void orangeBothSweetRoundAndOrangeColorWithMatchers() throws Exception {
  assertThat(someFruitList, everyItem(both(round()).and(sweet()).and(hasColor(Color.ORANGE))));
}

Как говорим, так и пишем - проверить каждый элемент нашим матчером. Возможны вариации - например, проверять что в пачке есть хотя бы один, удовлетворяющий условию - hasItem()

Примеры работы с коллекцией

About

Examples for library matchers-java to show how to write and work with matchers

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages