Этот пост – первый из цикла об этапах написания 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() {}
Теперь дело за имплементацией 🙂