В прошлом тексте про ассерты было упущено и недоговорено нечто важное.

В чем состоит различие между given и when и как это связано с ассертами?

Идея, лежащая в основе этого проста — мы хотим ограничить количество ассертов.

Рассмотрим небольшой пример.

Пускай мы передаем в метод некоторый валидный объект, вызываем дополнительный сервис и обогащаем его свойства.

Я не должен писать код вперед теста, поэтому будем считать, это для только для передачи общей картины.

public User enrichUser(User validUser){
      user.setDetails(enrichmentService.getUserDetails(validUser.getId()));
      return user;
}


Нас не интересуют вариации validUser'а. Он не нулевой, у него всегда есть id, на то он и valid. Это и есть precondition, т.е. given.

Фактически нам надо рассмотреть два условия — enrichmentService fail и success. Это condition, т.е. when.

Как отличить одно от другого? Given не требует проверки других кейсов, when — требует. Т.е. whenValidUser требует пары whenInvalidUser, а givenValidUser — не требует.

А enrichmentservice==null? Если мы инжектировали зависимость, то можно считать это частью конфигурации и не думать об этом в тестах. Совсем. Есть preconditions, которые не имеет смысла перечислять.

Если метод принимает просто user, количество проверяемых сценариев увеличивается. Given становится when.

Еще вопрос — но ведь, строго говоря, случайно или намеренно метод может испортить нам юзера? Неужели мы не должны проверять его?

Если мы отвечаем — да, должны, нам придется признать, что это может быть большое бремя. У юзера может быть пятьдесят свойств, и проверять их в каждом методе может быть накладно. Есть балк-проверки с помощью библиотек, но тем самым мы теряем важную функцию теста — предписывать желаемую функциональность, а не подбирать грязь за кривой имплементацией.

Это можно сравнить с аэропортовской секьюрити — кто-то проверяет билет, кто-то багаж, кто-то карманы, и проверки не дублируются на каждом шаге. Т.е. ассерты доверяют тому, что будет делать имплементация, и проверяют только то, что предписывают.

Таким образом, правильно построенный precondition сокращает количество сценариев и ассертов. Мы делаем ассерты только на condition.

К сожалению, фреймворки, как Cucumber, особого различия не делают, поскольку сами precondition не проверяют и для них что Given, что When — одно и то же, чисто описательный термин.

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

Это хороший повод реструктурировать спецификации и поправить дизайн.

Комментарии (3)


  1. Nichola
    14.11.2019 08:20

    Немного сумбурно всё написано:
    0. Given-When-Then они однозначно соответствуют сущностям используемым в большинстве классических определений теста (состояние до — действие — состояние после). Идея в этом, а упомянутое ограничение количества ассёртов это скорее следствие (опциональное и связанное с реализацией).
    1. Смесь английского, русского и комбинированного в заголовке как бы намекают что дальше легче не будет, но
    2. Хочу предупредить других читателей, что им может таки быть интересно проверить работу с invalid user переданным как validUser именованным параметром.

    Нас не интересуют вариации validUser'а. Он не нулевой, у него всегда есть id, на то он и valid. Это и есть precondition, т.е. given.

    public User enrichUser(User validUser){
    user.setDetails(enrichmentService.getUserDetails(validUser.getId()));
    return user;
    }


    1. rudnevr Автор
      14.11.2019 09:26

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


      А насчёт инвалид юзера, если идти в эту сторону, то придётся проверять и что сервис на самом деле делает, и детали его откуда. И в том ли мы приложении и том ли контексте. Т. е. у паранойи предела нет.


    1. rudnevr Автор
      14.11.2019 09:27

      А насчёт given — так я вообще не про теорию, я про то как пользоваться и зачем.