Недавно у меня появился аккаунт на Хабре и я опубликовал на статью “Не обманывайте своих заказчиков”. Было много обсуждений в комментариях и я решил сделать более детальное рассмотрение данной темы.
Я достаточно много занимаюсь не только разработкой, но и постановкой процессов, в том числе тестирования. И всегда несколько скептически относился к ручному тестированию, точнее к той его части, которая отвечает за «обеспечение работоспособности существующей фунциональности» (в простонародье регрессионное тестирование). Что же плохого в этом тестировании и почему многие компании его тогда используют?
Корень проблемы
Проблема достаточно несложная, но ее суть не лежит на поверхности. Представьте себя на месте заказчика. Вы приходите с набором требований к исполнителю, в данном случае будем рассматривать в роли исполнителя команду разработки. Вы договорились о всех деталях с командой и она готова начать работать над вашим продуктом. Вы как умный и опытный заказчик грамотно расставили приоритеты и выдаете команде требование за требованием в правильном для бизнеса порядке. Вроде пока все звучит неплохо и ситуация не напоминает проблематичную.
Но вот проходит месяц-другой и выясняется неприятная деталь — на тестирование тратится все больше и больше времени. Оно вполне логично — ведь готовой функциональности в продукте становится все больше и надо постоянно контролировать, что она по-прежнему работает. Это эффект называется «регрессионная спираль смерти» (термин подсмотрен в выступлении Макса Дорофеева «Обезьянки против Роботов»). Эта спираль развивается со временем и становится все шире и шире. И если раньше тестировщики успевали «пробежаться» по продукту за несколько часов, то вскоре на это начинает уходить несколько дней.
Ну и где же тут обман?
Все очень просто — отдавая заказчику «готовую» работу мы не даем ему способа бесплатно и легко контролировать ее работоспособность в будущем. Утрируя, это может звучать так: «Мы закончили работать над X, Y и Z. Но уже через пару недель есть шансы, что они перестанут работать нормально…» Но как же так? Ведь заказчик заплатил за полное завершение работ. Как же ему теперь быть? Ему предлагается очень простое решение — плати нам постоянно дополнительную плату за то, что мы будем контролировать работоспособность на протяжении жизни продукта.
Получается, что стоимость одной конкретной функциональности будет продолжать расти на протяжении всего проекта. И чем быстрее работает команда, тем быстрее будет расти стоимость затрат на поддержание регрессии. Это же нечестно! В жизни бы мы никогда не согласились на такое. Представляете, вы заказываете ремонт, а вам предлагают платить дополнительные деньги за то, чтобы при работе над кухней не развалилась гостиная…
Кому это все нужно?
Почему же при всей ущербности подобной модели она не перестанет применяться? Тут есть несколько причин:
- Эта модель выгодна аутсорсинговым компаниям. Ведь в этом случае заказчик «попадает в рабство» и постоянно платит даже за то, что уже давно оплачено и должно работать. А как только регрессионная спираль выходит на новый большой виток, то можно нанять еще тестировщиков, к ним тест-лида, тест-менеджера и понеслась…
- Многим заказчикам объясняют, что «это нормальный подход в IT» и так все работают. Многие из них даже не догадываются об обмане и соответственно не пытаются с ним бороться.
- Так исторически сложилось в компании и она делает все проекты «по накатанной». Никто не пытается проанализировать варианты улучшения процесса разработки, а уж тем более воплотить их в жизнь.
При этом, во многих современных продуктовых компаниях к такого рода затратам относятся скептически и не поддерживают идею ручного регрессионного тестирования. В таких компаниях нет позиции monkey тестировщика, так привычной для рынка аутсорсинга.
Решение проблемы
Для того, чтобы работать честно, надо пересмотреть «критерии готовности» команды разработки и расширить их наличием автоматизированных приемочных тестов. Перед работой над определенной функциональностью хорошая команда (заметьте, я не использовал слово Agile) задает заказчику вопросы о том, как функциональность должна работать и как заказчик будет проверять готовность. Это и есть процесс формирования приемочных критериев. Они представляют из себя мини-контракт между заказчиком и командой на реализацию этой функциональности.
Речь идет о работе над требованиями на очень короткий срок — обычно одна итерация. Если заказчик не может на такой срок сформулировать требования во всех деталях и ответить на все вопросы, то у вас уже проблемы. Работа над приемочными тестами помогает выявить их на самой ранней стадии.
Не стоит ожидать от заказчика, что он придет и выложит вам приемочные критерии на блюдечке. Вопросов «а как вы проверите, что это работает», «а давайте рассмотрим на примерах», «а как это будет использоваться» в умелых руках достаточно, чтобы получить набор критериев на практике. Главное чтобы не приходилось в процессе разработки делать предположения и обращаться к “здравому смыслу”. Это очень опасно и может привести к переделкам и недовольству заказчика.
Дальше хорошая команда снабжает эти критерии приемки конкретными примерами, данными и «прикручивает» к работающему продукту. Таким образом, добавляется возможность с помощью приемочных тестов в любой момент времени проверить, работает ли та или иная функциональность в продукте после любых изменений. Запустить эти автоматизированные приемочные тесты может любой, обычно они добавляются к Continuous Integration серверу и запускаются на каждое изменение или в ручном режиме.
Почитайте как работает на примере одной команды командное написание приемочных тестов и про эволюцию необходимости таких тестов к запуску в облаке.
Но ведь есть же модульные тесты? Зачем еще раз делать ту же работу? Модульными тестами хороший разработчик покрывает код, чтобы убедиться, что его точечная идея для класса, функции, метода или их связки работает правильно. К сожалению, модульные тесты не способны обеспечить проверку даже возможности запуска приложения, не говоря уже о его функциях. Плюс, приемочные тесты написаны на языке, понятном заказчику, в отличии от модульных тестов. Если искать связь, то модульные тесты рождаются из приемочных, в то же время играя роль приемочных тестов на уровне кода.
Преимущества
И теперь никто не тратит время на ручное регрессионное тестирование. Это время тратится на исследовательское тестирование, на помощь команде разработки завершить свою работу вовремя и с высоким уровнем качества, на обеспечение работоспособности канала донесения требований от заказчика к команде разработки. Заказчик при этом имеет под рукой мощный инструмент контроля качества и работоспособности своего продукта, при этом понимая, что заплатил за это не напрасно.
Кто-то может подметить, что понадобится время на написание и поддержку такого рода автоматизированных тестов. Затраты на написание конечно же включаются в оценки выполнения работ по разработке. А вот стоимость поддержки будет зависеть напрямую от уровня команды. Грамотно написанные тесты будут основаны на правильных инструментах и фреймворках, которые абстрагируют тестовую логику от деталей вашего продукта и дают возможность изменять что-то только при серьезных изменениях в продукте. И пусть себе требования на здоровье меняются, но скорее всего не вся функциональность перепиливается каждую неделю, особенно в большом проекте.
Не все легко покрыть автотестами. Но из моего опыта, люди зачастую просто не знают, не хотят или не умеют этого делать. Для веб проектов есть Selenium, для совершенно произвольных есть Sikuli. А еще уйма специализированных инструментов. Можно замечательно с помощью Selenium тестировать UI (расположение элементов, верстку, отработку JavaScript). Советую посмотреть мое выступление на одной из конференций по поводу подобного тестирования. В разделе материалов можно найти больше на эту тему. Если не получается протестировать через конечный пользовательский UI, то можно тестировать API бизнес логики. Это все равно будет лучше чем ничего. Тогда ручное тестирование может быть сосредоточено больше на тестировании UI слоя.
Есть еще одно преимущество от наличия таких тестов – возможность делать рефакторинг. Рефакторинг — последовательность изменений, которая изменяет внутреннюю структуру программы без изменения ее внешнего поведения. При рефакторинге тесты не меняются. Более того, без них невозможно убедиться, что внешнее поведение не изменилось, а значит называть это рефакторингом.
Эти же тесты придут вам на помощь и на этапе сопровождения. На этом этапе часто меняется только часть функциональности, а остальная должна продолжать работать стабильно. И вот тогда польза от приемочных тестов колоссальная. Мало того, по ним можно понять как должна была работать та или иная часть приложения. Потому что доменные знания теряются за документами, в которых устаревают практически моментально.
Заключение
Задумайтесь, стоит ли продолжать обманывать своего заказчика. Или лучше работать так, чтобы можно было гордиться результатами своего труда, при этом убирая максимум скучной ручной работы и оставляя только интересную творческую составляющую? Если вы выбираете второй подход, то вам нужно обеспечить прозрачность и расписать все плюсы и минусы на самом раннем этапе. На более поздних этапах вводить описанное в статье тестирование может быть гораздо труднее и затратнее.
Не хочешь пропускать ничего интересного? Подпишись на ленту RSS или следи за нами в Twitter!