Я работал с группами, имевшими разные представления о терминах «выполнено» и «готово». В одной группе использовались термины «сделано» и «совсем сделано». У профессиональных разработчиков есть только одно определение: выполнено – значит выполнено. Сделано – значит, что весь код написан, все тесты пройдены, служба контроля качества и ключевые участники приняли результат. Сделано.
Но как добиться такого уровня выполнения, не снижая темпа перехода между итерациями? Нужно создать набор автоматизированных тестов, прохождение которых означает выполнение всех перечисленных условий! Если приемочные тесты вашей подсистемы проходят успешно, значит, работа выполнена.
Профессиональные разработчики расширяют определение требований до автоматизированных приемочных тестов. Они общаются с ключевыми участниками и специалистами по контролю качества, стремясь к тому, чтобы автоматизированные тесты полностью отражали определение «выполнения».
Сэм: «Так, еще нужно организовать резервное копирование журнальных файлов».
Пола: «С какой частотой?»
Сэм: «Ежедневно».
Пола: «Понятно. И где будут храниться резервные копии?»
Сэм: «В смысле?»
Пола: «Наверное, они должны сохраняться в определенном подкаталоге?»
Сэм: «Да, пожалуй».
Пола: «И как он будет называться?»
Сэм: «Может, backup?»
Том (тестер): «Погодите, backup – слишком общее название. Что именно будет храниться в этом каталоге?»
Сэм: «Резервные копии».
Том: «Резервные копии чего?»
Сэм: «Журнальных файлов».
Пола: «Но ведь журнальный файл всего один?»
Сэм: «Нет, их много. По одному на каждый день».
Том: «То есть у нас будет один активный журнал и много резервных копий?»
Сэм: «Конечно».
Пола: «О! А я думала, копии будут постоянно замещаться».
Сэм: «Нет, заказчик хочет, чтобы они хранились неограниченно долго».
Пола: «Для меня это новость. Хорошо, что вовремя разобрались».
Том: «Имя подкаталога должно сообщать, что именно в нем хранится».
Сэм: «Там будут храниться старые неактивные журналы».
Том: «Тогда мы назовем его old_inactive_logs».
Сэм: «Отлично».
Том: «И когда будет создаваться этот каталог?»
Сэм: «В смысле?»
Пола: «Каталог будет создаваться при запуске системы, но только в том случае, если он не существует».
Том: «Понятно, это наш первый тест. Я запускаю систему и проверяю, создан ли каталог old_inactive_logs. Затем я добавляю файл в этот каталог, завершаю работу, снова запускаю систему и проверяю, что каталог и файл находятся на положенном месте».
Пола: «На выполнение этого теста уйдет много времени. Инициализация системы уже занимает около 20 секунд, и время только растет. К тому же я не хочу собирать систему заново при каждом запуске приемочных тестов».
Том: «Что ты предлагаешь?»
Пола: «Создадим класс SystemStarter. Основная программа будет загружать его с группой объектов StartupCommand, построенных на базе паттерна «Команда». Затем во время запуска системы SystemStarter просто приказывает всем объектам StartupCommand выполнить свои команды. Один из этих объектов StartupCommand создает старый каталог old_inactive_logs, но только в том случае, если он не существует».
Том: «Ладно, тогда мне остается только протестировать этого потомка StartupCommand. Я напишу для этого простой тест FitNesse».
(Том идет к доске.)
«Первая часть будет выглядеть примерно так»:
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
«А вторая часть будет примерно такой»:
given the command LogFileDirectoryStartupCommand
given that the old_inactive_logs directory exists and that it contains a file named x
When the command is executed
Then the old_inactive_logs directory should still exist and it should still contain a file named x
Пола: «Да, этого должно хватить».
Сэм: «А это все действительно необходимо?»
Пола: «Сэм, какое из этих двух утверждений недостаточно важно для проверки?»
Сэм: «Просто я хочу сказать, что для написания всех этих тестов потребуется немало лишней работы».
Том: «Да, но не больше, чем для написания плана ручного тестирования. И намного меньше, чем для многократного выполнения тестов вручную».
Взаимодействие сторон
Основные цели приемочных тестов – взаимодействие сторон, ясность и точность требований. В результате согласования приемочных тестов разработчики, ключевые участники и тестеры достигают понимания планируемого поведения системы. Достижение такой ясности – обязанность всех сторон. Профессиональные разработчики считают своей обязанностью работать с ключевыми участниками и тестерами и принимать меры к тому, чтобы все стороны знали, что именно они собираются построить.
Автоматизация
Приемочные тесты всегда должны быть автоматизированными. В других моментах жизненного цикла программных продуктов находится место для ручного тестирования, но такие тесты никогда не должны выполняться вручную. Причина проста: затраты.
Взгляните на рис. 7.1. Руки, которые вы на нем видите, принадлежат менеджеру по контролю качества крупной интернет-компании. В документе, который он держит, содержится оглавление его плана ручного тестирования. Он оплачивает целую армию тестеров из других стран, которые выполняют этот план каждые шесть недель. Каждое тестирование обходится примерно в миллион долларов. Он только что вернулся с собрания, на котором руководитель сообщил, что бюджет тестирования будет урезан примерно на 50 %, а теперь спрашивает меня: «Какую половину этих тестов не нужно выполнять?»
Назвать происходящее катастрофой значило бы не сказать ничего. Затраты на ручное тестирование настолько велики, что фирма решила отказаться от него – и просто жить дальше, не зная, работает ли половина ее продукта!
Рис. 7.1. План ручного тестирования
Профессиональные разработчики не допускают возникновения подобных ситуаций. Затраты на проведение автоматизированных тестов настолько малы по сравнению с затратами на ручное тестирование, что написание сценариев, запускаемых вручную, не имеет никакого экономического смысла. Профессиональные разработчики считают своей обязанностью проследить за тем, чтобы приемочные тесты проводились в автоматизированном режиме.
Существует множество программных инструментов (как коммерческих, так и с открытым кодом), автоматизирующих приемочные тесты. FitNesse, Cucumber, cuke4duke, robot framework и Selenium – этот список далеко не полон. Во всех этих инструментах автоматизированные тесты определяются в такой форме, что даже не-программисты могут читать их, понимать и даже создавать.
Дополнительная работа
Замечание Сэма насчет лишней работы понятно. На первый взгляд кажется, что написание подобных приемочных тестов потребует значительных усилий. Но взглянув на рис. 7.1, мы видим, что эту работу неправильно называть «лишней». Написание тестов всего лишь является работой по определению спецификации системы. Только на таком уровне детализации мы, программисты, понимаем, что означает «выполненная работа». Только на таком уровне детализации ключевые участники проекта могут убедиться в том, что система, за которую они платят, делает то, что требуется. И только на таком уровне детализации возможна успешная автоматизация тестирования. Так что не стоит рассматривать эти тесты как лишнюю работу – лучше рассматривайте их как значительную экономию времени и денег. Тесты предотвратят ошибки в реализации системы и помогут узнать, когда ваша работа закончена.
Кто и когда пишет приемочные тесты?
В идеальном мире ключевые участники проекта и служба контроля качества сотрудничают в написании этих тестов, а разработчики проверяют их на логическую непротиворечивость. В реальном мире ключевые участники редко находят время или желание погружаться на нужный уровень детализации, поэтому они перепоручают эту обязанность бизнес-аналитикам, специалистам по контролю качества или даже разработчикам. Если окажется, что тесты должны писать разработчики, по крайней мере проследите за тем, чтобы это были не те разработчики, которые занимаются реализацией тестируемой функциональности.
Бизнес-аналитики обычно пишут «оптимистичные» версии тестов, потому что эти тесты описывают аспекты, обладающие коммерческой ценностью. Служба контроля качества обычно пишет «пессимистичные» тесты с проверкой всевозможных граничных условий, исключений и аномальных случаев. И это понятно, потому что задача контроля качества – думать о том, что может пойти не так.