Бизнес-аналитики обычно пишут «оптимистичные» версии тестов, потому что эти тесты описывают аспекты, обладающие коммерческой ценностью. Служба контроля качества обычно пишет «пессимистичные» тесты с проверкой всевозможных граничных условий, исключений и аномальных случаев. И это понятно, потому что задача контроля качества – думать о том, что может пойти не так.
Согласно принципу «поздней точности» приемочные тесты следует писать как можно позднее, обычно за несколько дней до реализации. В проектах на базе гибких методологий тесты пишутся после выбора функций для следующей итерации или спринта.
Первые приемочные тесты должны быть готовы к первому дню итерации. Новые тесты должны появляться ежедневно вплоть до середины итерации, когда готовы должны быть все тесты. Если к середине итерации некоторые приемочные тесты еще не готовы, переведите нескольких разработчиков на их срочную доработку. Если это происходит часто, включите в команду дополнительных бизнес-аналитиков и/или специалистов по контролю качества.
Роль разработчика
Работа по реализации некоторого аспекта системы начинается тогда, когда для этого аспекта готовы все приемочные тесты. Разработчики выполняют приемочные тесты и убеждаются в том, что те не проходят. Затем они связывают приемочные тесты с системой и начинают работать над реализацией нужного аспекта, обеспечивая прохождение приемочных тестов.
Пола: «Питер, поможешь мне?»
Питер: «Разумеется, а что случилось?»
Пола: «Вот наш приемочный тест. Как видишь, он не проходит».
given the command LogFileDirectoryStartupCommand
given that the old_inactive_logs directory does not exist
when the command is executed
then the old_inactive_logs directory should exist and it should be empty[35]
Питер: «Да, все результаты красные. Ни один сценарий еще не написан. Давай я напишу первый»:
|scenario|given the command _|cmd|
|create command|@cmd|
Пола: «А у нас уже есть операция createCommand»?
Питер: «Да, в пакете CommandUtilitiesFixture, который я написал на прошлой неделе».
Пола: «Хорошо, давай запустим тест».
Питер: (запускает тест) «Первая строка стала зеленой, переходим к следу ющей».
Не обращайте внимания на все эти сценарии (scenarios) и оснастки (fixtures) – это всего лишь служебный код, который необходимо написать для связывания тестов с тестируемой системой. Достаточно сказать, что все перечисленные инструменты предоставляют те или иные средства поиска по шаблону для распознавания и разбора инструкций теста и последующего вызова функций, передающих данные тестируемой системе. Все это делается достаточно просто, а полученные сценарии и оснастки могут использоваться в разных тестах.
Суть в том, что разработчик должен связать приемочные тесты с системой, а затем обеспечить их прохождение.
Обсуждение тестов и пассивно-агрессивная позиция
Авторы тестов – люди, и они тоже допускают ошибки. Иногда при переходе к реализации становится очевидно, что тест выглядит бессмысленно. Тесты бывают слишком запутанными или громоздкими.
Они могут базироваться на нелепых предпосылках. А иногда они попросту неверны. Все это может быть весьма неприятно, если вы – разработчик, который должен обеспечить прохождение теста.
Ваша задача как профессионального разработчика – обсудить ситуацию с автором теста для его улучшения. Никогда не выбирайте пассивно-агрессивную позицию, когда вы говорите себе: «Как написано в тесте, так я и сделаю».
Помните: профессионал обязан помочь своей группе создать программу настолько качественную, насколько это возможно. А это означает, что все должны уделять внимание возможным ошибкам и промахам коллег и совместно работать над их исправлением.
Пола: «Том, с этим тестом что-то не то».
ensure that the post operation finishes in 2 seconds.
Том: «По-моему, все нормально. Наше требование гласит, что пользователи не должны ждать больше двух секунд. В чем проблема?»
Пола: «Проблема в том, что мы можем гарантировать выполнение требования только в статистическом смысле».
Том: «По-моему, это только слова. В требованиях ясно сказано: две секунды».
Пола: «Верно, и мы можем обеспечить этот результат в 99,5 % случаев».
Том: «Пола, в требовании этого нет».
Пола: «Мы живем в реальном мире. Я не могу предоставить других гарантий».
Том: «Сэм будет в бешенстве».
Пола: «Вообще-то я с ним уже пару раз обсуждала эту тему. Он согласен, если типичная продолжительность операции будет не более двух секунд».
Том: «Ну и как мне писать этот тест? Я же не могу сказать: „операция обычно заканчивается за две секунды“».
Пола: «Сформулируй на статистическом уровне».
Том: «Предлагаешь выполнить операцию тысячу раз и проверить, что она заняла более двух секунд в пяти и менее случаях? Абсурд».
Пола: «Нет, это займет слишком много времени. А как насчет этого?»
execute 15 post transactions and accumulate times.
ensure that the Z score for 2 seconds is at least 2.57[36]
Том: «А что еще за z-показатель?»
Пола: «Так, статистика. А как тебе такая формулировка?»
execute 15 post transactions and accumulate times.
ensure odds are 99.5 % that time will be less than 2 seconds[37]
Том: «Да, это уже понятнее, но можно ли доверять математике?»
Пола: «Я обязательно включу все промежуточные вычисления в отчет по тестированию, чтобы ты мог проверить вычисления, если у тебя останутся сомнения».
Том: «Хорошо, меня это устроит».
Приемочные тесты и модульные тесты
Не путайте приемочные тесты с модульными (unit tests). Модульные тесты пишутся программистами для программистов. Они представляют собой формальные архитектурные документы с описанием нижнего уровня структуры и поведения кода. Их читателями являются не бизнесмены, а программисты.
Приемочные тесты создаются бизнесменами для бизнесменов (даже если в конечном итоге их пишете вы, разработчик). Они представляют собой формальные описания требований, определяющие поведение системы с точки зрения бизнеса. Их читателями являются бизнесмены и программисты.
Возможно, у кого-то возникнет соблазн избавиться от лишней работы, предположив, что тесты двух видов избыточны. Но хотя модульные и приемочные тесты часто тестируют одно и то же, никакой избыточности в них нет.
Во-первых, даже если они тестируют одно и то же, при этом используются разные пути и механизмы. Модульные тесты углубляются во внутреннюю реализацию системы и вызывают методы конкретных классов. Приемочные тесты обращаются к системе на значительно более высоком уровне – уровне API или даже уровне пользовательского интерфейса. Таким образом, пути выполнения этих тестов сильно различаются.
Но настоящая причина, по которой эти тесты нельзя назвать избыточными, заключается в том, что тестирование не является их главной функцией. Тот факт, что они что-то проверяют, вторичен. Модульные и приемочные тесты прежде всего являются документами и лишь потом – тестами. Их главная цель – формальное документирование архитектуры, структуры и поведения системы. Автоматическая проверка архитектуры, структуры и поведения чрезвычайно полезна, но истинной целью является именно документирование.
Графические интерфейсы и другие сложности
Графический интерфейс трудно определить заранее. Теоретически это возможно, но редко удается сделать хорошо. Дело в том, что эстетика – предмет субъективный, а следовательно, переменчивый. Разработчики любят повозиться со своими графическими интерфейсами, доводить их до ума и шлифовать. Они хотят использовать разные шрифты, цвета, макеты и схемы выполнения операций. Графические интерфейсы находятся в постоянном развитии.
Это обстоятельство усложняет написание приемочных тестов для графических интерфейсов. Задача решается таким проектированием системы, при котором графический интерфейс можно было бы рассматривать как API, а не как набор кнопок, ползунков, таблиц и меню. На первый взгляд кажется странно, но в действительности речь идет просто о качественном проектировании.
В области проектирования программных систем существует принцип, называемый принципом единственной обязанности (SRP, Single Responsibility Principle). Он гласит, что при проектировании следует разделять аспекты системы, которые могут изменяться по разным причинам, и группировать вместе те аспекты, которые изменяются по одним и тем же причинам. Графические интерфейсы не являются исключением.
Макет, формат и схема выполнения операций в графическом интерфейсе могут меняться по эстетическим причинам и по соображениям эффективности, но базовая функциональность графического интерфейса остается неизменной. Таким образом, при написании приемочных тестов для графического интерфейса следует пользоваться базовыми абстракциями, которые изменяются относительно редко.