Несмотря на то, что технологии модульного тестирования существуют уже 30 лет (в 1989 году Кент Бек написал статью “Simple Smalltalk Testing: With Patterns”), тем не менее не все программисты владеют этой технологией и не все компании сделали автоматическое тестирование частью своей корпоративной культуры. Даже несмотря на очевидные преимущества автоматического тестирования, все равно поведенческое сопротивление достаточно сильное. Кто пробовал внедрять автоматические тесты, тот знает, что всегда найдется какая-то причина, почему это не удалось сделать.
Из моего личного опыта внедрения методов надежного программирования в своей компании, в компаниях, которые я консультировал, общения на конференциях, а также из публично доступных источников, я сформулировал типичные возражения и сопротивления, которые препятствуют внедрению культуры автоматического тестирования.
Все возражения я сгруппировал в пирамиду надежного программирования, которая включает четыре уровня:
- Профессиональная культура (самый высокий уровень, основа надежного программирования) — совокупность норм, неписаных правил, убеждений сотрудника, которыми он руководствуется в работе. Например: “Отправить в репозиторий код непокрытый тестами — плохо”, “Замолчать о найденных ошибках в коде — стыдно”.
- Менеджмент — это процедуры, политики, правила, принятые в организации, а также воля (решения) руководителей. Например: “Каждая разработанная функция приложения должна пройти код ревью. Без исключений!”.
- Методы — это научные подходы, способы решения той или иной задачи. Например: “Если функцию приложения сложно протестировать, нужно повысить тестопригодность приложения, применив шаблон Dependency Injection”.
- Технологии (самый низкий уровень) — это языки программирования, библиотеки, фреймворки, инструменты. Например, JUnit, Selenium, XCTest и так далее.
Зачем нужно такое деление? Потому что проблема одного уровня решается методами этого же уровня или методами более высокого уровня. Например, если в организации не принято писать автоматические тесты (проблема профессиональной культуры), то эту проблему невозможно решить подробно описав бизнес-процесс тестирования (уровень “менеджмент”) или установив современный фреймворк (уровень “технологии”). Даю гарантию, что через неделю никто писать тесты не будет, невзирая на утвержденный бизнес-процесс.
Возражения культурного уровня
“Мои программы не ломаются. Я не вижу нужды в тестировании”.
Такое утверждение я слышал от начинающих или чрезмерно самоуверенных программистов. Разумеется, единожды написанная функция не может сломаться сама по себе. Но здесь важно понимать, что со временем программе может потребоваться поддержка, внесение новых функций или дополнения в существующие функции. Сложность программ — количества классов и зависимостей между ними — достаточно большая, и со временем, после внесения очередной новой функции или улучшения существующей, ошибка рано или поздно произойдет. Автоматический тест позволил бы выявить такую регрессию.
Кроме этого, часто такое возражение можно услышать от начинающих программистов, которые не имеют понятия тестировании. Например, поломкой считают только креши, а не функциональные ошибки.
Ненадежно!
— Это простое приложение, здесь нечему ломаться, поэтому мы не будем писать автоматические тесты.
На одном из собеседований, которое я проводил, произошел такой диалог:
— Вы владеете навыками автоматического тестирования?
— Нет, я писал простые программы, там нечему было ломаться.
— В чем ваша мотивация сменить место работы?
— Я хочу писать сложные приложения.
Я очень хорошо знаю, чем это заканчивается. Программисту доверяют разработку более сложной программы, но он не владеет методами автоматического тестирования, качественно протестировать приложение не может, и справиться с масштабом проекта не может, что в результате приведет к срыву проекта, перерасходу бюджета разработки, утраты репутации. Потому что я сам лично руководил проектами, где я не смог совладать с масштабом проектом и завалил его именно из-за отсутствия автоматических тестов.
Нежелание брать ответственность за качество кода, за тестирование.
Автоматические тесты — единственный источник оперативной и объективной информации об истинном качестве программного продукта. Иными словами за спиной у программиста всегда стоит надсмотрщик, который в любой момент времени может доложить руководству о том, насколько качественно программист выполняет свою работу. Автоматические тесты позволяют увязать результативность труда не с закрытыми тикетами в Джире, а с истинным качеством программного продукта. А здесь уже нужно думать о том, как писать надежно, чтобы каждое очередное изменение кода не ломало существующие функции. Чтобы каждая новая функция работала не только в сценарии, когда все хорошо, но и корректно обрабатывает ошибки.
Ответственность — это добровольное принятие обязательств обеспечить положительный результат труда. Сотрудник принимает это обязательство в силу своего характера и воспитания. К сожалению, из-за культурного и профессионального кризиса не каждый программист готов взять на себя такие обязательства.
“Пишите сразу правильно без ошибок”
У людей, которые не очень хорошо знакомы с тем, как происходит разработка программного обеспечения, может возникнуть негативное отношение к разработчикам, которые упоминают какие-то ошибки.
— Давайте покроем приложение автоматическими тестами.
— Зачем?
— Чтобы убедиться, что все работает корректно и нет ошибок.
— Вы пишите с ошибками? У вас низкая квалификация? Пишите сразу правильно без ошибок.
— Да, но все допускают ошибки…
— А вот друга компания XYZ сказала, что у них топ-программисты, которые пишут без ошибок!
Таким образом, разработку тестов сложно “продать” заказчикам, которые не подкованы технически. В результате, менеджмент вынужден разрабатывать проект без автоматических тестов, что приходит к известным проблемам.
Возражения на уровне менеджмента
“C тестами программу писать в два раза дольше. Мы не уложимся в сроки”.
С первого взгляда этот тезис кажется справедливым. На написание тестов действительно нужно потратить время программиста. Но программисты и менеджмент не учитывают, что в суммарное время разработки продукта входит не только программирование, но отладка и поддержка, а также огромная стоимость ручного регрессионного тестирования после внесения исправлений.
У автоматических тестов есть несколько функций:
- Проверяющая.
- Тесты проверяют, корректно ли работает объект тестирования.
- Тесты проверяют качество работы программиста: решена ли поставленная задача, нет ли побочных эффектов в виде регрессий.
- Диагностирующая. Диагностические тесты позволяют существенно сократить время на поиск дефекта. Тесты позволяют определить место ошибки с точностью до класса и метода, а иногда и с точностью до строчки кода.
- Автоматизирующая. Тесты позволяют быстро и легко вводить объект тестирования в нужное состояние для отладки.
- Документирующая.
- Приемочные тесты фиксируют требования заказчика к разрабатываемому продукту.
- Тесты показывают примеры использования разрабатываемого компонента, тем самым сокращая время на изучение работы системы другим программистом.
В одной из организаций, которую я консультировал, менеджер сопротивлялся внедрению культуры автоматического тестирования:
— Но ведь тесты писать долго! Мы не уложимся в сроки!
— У вас есть ошибки, которые вы очень долго искали и исправляли?
— Да, есть такие.
— Какой самый сложный случай?
— Одну ошибку мы искали 80 часов.
— 80 часов — это две недели работы программиста. Если бы вы потратили даже целую неделю на автоматизацию тестированию, то это сэкономило бы вам месяцы на диагностировании и отладке вашего приложения!
В нашей организации есть постулат: “С тестами писать программу в два раза быстрее!” и этот постулат не обсуждается. Обсуждается только коэффициент 2 — иногда бывает и 3, и 4. А некоторые проекты просто невозможно завершить без грамотного автоматического тестирования (см. заваленный проект).
“У нас уже есть отдел ручного тестирования, пусть они и тестируют”.
С первого взгляда разделение специализаций на тестирование и программирование кажется логичным.
Но давайте рассмотрим недостатки ручного тестирования:
- Стоит очень дорого.
- Выполняется очень долго. Например: тестовые сценарии для мобильного приложения “Онлайн кинотеатр” тестер делает 40 часов. И это только для одной платформы! Если нужно протестировать приложение на iPhone, iPad, Apple TV, Android, Fire TV, то нужно потратить 40 × 6 = 240 часов рабочего времени, это полтора месяца, что неприемлемо для коротких циклов разработки.
- Ручное тестирование подвержено обычным человеческим ошибкам — не дает объективного и истинного результата.
Более того, некоторые виды тестов невозможно выполнить в разумные сроки, потому что количество комбинаций форматов и различных тестовых сценариев очень велико. Например:
- Функция импорта CSV-файлов.
- Парсеры текстовых документов.
- Финансовые инструменты.
Возражения на уровне методов
Незнание методов автоматического тестирования.
Из-за кризиса образования в вузах нигде нет дисциплин автоматического тестирования. Таких же курсов очень мало в коммерческих айти-школах. А существующие курсы — поверхностные и низкого качества. Поэтому, я часто встречал ступор у программистов: они не знают, как протестировать нетривиальные приложения (сложнее, чем 2 + 2 = 4).
На самом деле наука о тестировании достаточно обширная. Например, не каждый программист сразу ответит на вопросы: а) что такое тестопригодность? б) что такое управляемость и наблюдаемость? в) какие шаблоны проектирования улучшают тестопригодность приложения? И так далее.
Программисты не знают, что они пишут, как оно выглядит, какие будут функции и интерфейсы.
Очень сложно протестировать то, что непонятно как выглядит. Иными словами, без заранее сформулированных требований к приложению, программист не может понять, что от него ожидают.
Особенность некоторых проектов в том, что они разрабатываются по технологии Minimum Viable Product, который иными словами можно охарактеризовать так: “Давайте сделаем хоть что-то за минимальное время и минимальный бюджет”, а программист рассматривается заказчиком или менеджментом как аналитик, дизайнер, архитектор, программист и тестировщик в одном флаконе. При таком подходе исключается формальный этап проектирования программной системы: определение бизнес-логики, предметной области, интерфейсов компонентов, а также их внутренней организации их отношений между ними. Нет формализованной архитектуры, нет интерфейсов, нет прописанных бизнес-процессов — непонятно, что тестировать, через какие интерфейсы и какой ожидаемый результат.
Нетестопригодный код.
Тестопригодность — свойство проекта, которое говорит: насколько легко его можно протестировать. Тестопригодность определяется двумя другими свойствами: управляемостью и наблюдаемостью. Управляемость — свойство, которое определяет насколько легко можно ввести приложение в нужное состояние для тестирования (выполнить предусловия). Наблюдаемость — как легко можно считать состояние после проведения теста, сравнить его с ожидаемым.
Например, двухфакторную аутентификацию с помощью СМС очень сложно протестировать автоматически, потому что функция получения СМС находится за рамками автоматизированной среды тестирования. Такая система нетестопригодна.
Сталкиваясь с нетестопригодной системой, программист опускает руки и избегает тестирование такой системы.
Надежно!
Закладывай тестопригодность системы на этапе проектирования!
Подготовка тестовых данных.
Одно из неочевидных сопротивлений — это подготовка тестовых данных и эталонов. Например: начальное состояние базы данных на которой производится тестирование. На подготовку тестовых данных может уходить много времени и рутинного труда, поэтому этот труд считается среди программистов неблагодарным и неинтересным.
Решение:
- разработка эталонных значений и примеров на этапе разработки приемочных тестов — они также помогут решить конфликты с заказчиком на этапе приема работ;
- разработка эталонных значений на этапе проектирования системы. Например, эталонные HTTP запросы и ответы — помогут легче интегрировать клиент и сервер;
- разработка специальных процедур сборки баз данных, при которых требуемое состояние базы данных создается автоматически, а не вручную
- использование шаблона Object Mother [Fowler, Schuh, Peter, and Stephanie Punke. “Easing Test Object Creation in XP.” XP Universe. 2003], который помогает легко аллоцировать и инициализировать объекты в требуемом состоянии.
Обслуживание тестов.
Во время разработки проекта к нему могут измениться требования (уточнение, изменение). Или может произойти внутренний рефакторинг, который приведет к изменению интерфейсов классов. С изменением требований изменятся и критерии приемки той или иной функции, а с ними и тесты. В какой-то момент программист может отказаться от обслуживания тестов — т. е. от поддержки их в актуальном состоянии.
Решение:
- использование шаблона “адаптер” для того, чтобы отвязать логику теста от того интерфейса, который он тестирует;
- использование высокоуровневых тестов (Gherkin, Cucumber, Given-When-Then); см. решение к сопротивлению “подготовка тестовых данных”.
Заключение
Нет сомнений, что программное обеспечение должно быть надежным: превосходить ожидания потребителей. Автоматические тесты — хоть и не единственный, но важный компонент при разработке надежного программного обеспечения.
Я сформулировал типичные возражения и преграды внедрению автоматического тестирования, с которыми я столкнулся лично в своей организации, а также в тех организациях, которые я консультировал.
Статья очерчивает только проблемы и едва затрагивает пути их решения. В целом стратегия решения упомянутых проблем мне кажется такой:
- Формирование и пропаганда новой культуры ИТ-проектирования, которая заключается в надежности, гордости и персональной ответственности за результат.
- Разработка новых высоких стандартов тестирования кода.
- Разработка и внедрение учебных курсов.
- Введение мотивации в карьеру программистов и менеджеров, увязанной с качеством разрабатываемых программных продуктов, а также с навыками автоматического тестирования.
Самое важное, что мне удалось понять — это то, что проблемы находятся на разных уровнях: технологическом, методическом, управленческом и культурном. И решать их нужно на соответствующих уровнях. Очень сложно внедрить автоматические тесты, если программист не обучен методам тестопригодного проектирования или если менеджмент не поддерживает культуру надежного программирования в организации.
Я буду благодарен примерам из вашей практики, насколько легко или насколько тяжело удалось внедрить автоматические тесты в вашей организации. С какими проблемами столкнулись именно вы? Как вы их решали?