Именование – первый этап написания unit test’а

Этот пост – первый из цикла об этапах написания unit test’а.

Жизненный цикл теста состоит далеко не только в его написании, а ещё и в том, что будет с ним происходить во время эволюции кода приложения. Когда тест будет становиться красным, перед некоторым разработчиком будет стоять задача разобраться в причине и устранить её. Наша же задача состоит в том, чтобы максимально облегчить жизнь этому разработчику из будущего. Поэтому я рекомендую привязывать имя теста к его содержанию.

Времена тестов Record-Repeat ушли, и сейчас шире используется структура Arrange-Act-Assert. Это мы и отразим в тесте, назвав его по шаблону WhenAct_AndArrange_ThenAssert. Наша цель – выбрать последовательно, какие осмысленные действия будут происходить в Act, Arrange и Assert и закрепить их в названии.

Разберём каждый шаг на примере. Нашим unit under test будет портал, который телепортирует героев между измерениями. Тестировать мы будем следующий функционал: Когда герой в шляпе проходит в другое изменение, резкий порыв ветра срывает с него шляпу и он её теряет.

Вот часть интерфейса портала, которая важна нам в этой статье:

public class Portal
{
    public void Teleport(IHero hero) {}
}

Как видите, я сознательно не указываю реализацию, т.к. сейчас она для нас интереса не представляет.

Act

Определимся с тестируемым методом. В данном случае это Teleport. Действие, которое он выполняет – телепортация героя. Запишем это в заголовок теста во времени present continuous “when teleporting hero..”.

Название нашего теста станет:

[Test]
public void WhenTeleportingHero_...() {}

Present continuous тут не совсем подходит с точки зрения правил написания английских предложений. Однако это даёт нам бонус в том, что алфавитная сортировка тестов совпадает с алфавитной сортировкой методов unit under test. Это очень упрощает навигацию по тестам и методам класса и даёт возможность получить представление о поведении класса глядя на класс и его тесты “с высоты птичьего полёта”, т.е. когда все методы схлопнуты.

public class Portal
{
    public void Activate() {}
    public void Deactivate() {}
    public void Teleport(IHero hero) {}
}
public class PortalTests
{
    [Test]
    public void WhenActivating…() {}

    [Test]
    public void WhenDeactivating…() {}

    [Test]
    public void WhenTeleporingHero…() {}
}

Arrange

Секция Arrange в тесте отвечает за дополнительные условия, которые не являются логическим умолчанием. Грубо говоря это какие-то отклонения в настройке unit under test или необычные входные данные. В данном примере наш unit under test работает в штатном режиме, однако есть дополнительное условие на герое: он в шапке – “hero wears hat”.

Это и продолжит название нашего теста:

[Test]
public void WhenTeleportingHero_AndHeroWearsHat_...() {}

Сразу упомяну, что когда таких специальных условий несколько (например, герой в шляпе упрямо пытается пройти в деактивированный портал), то, для облегчения жизни нашего гипотетического программиста из будущего, всё из этого надо отразить в названии теста:

[Test]
public void WhenTeleportingHero_AndHeroWearsHat_AndPortalIsDeactivated_...() {}

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

Assert

Эта секция теста содержит набор проверок правильности логики работы unit under test. В нашем примере мы ожидаем, что герой потеряет свою шапку – “hero should lose hat”.

Название нашего теста полностью готово:

[Test]
public void WhenTeleportingHero_AndHeroWearsHat_ThenHeroShouldLoseHat() {}

Теперь дело за имплементацией 🙂

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s