Как доказать важность тестов каждому участнику проекта
Представьте, вы за два дня полностью реализовали новую фичу. Код написан, он работает и все классно. Ваш менеджер говорит, что можно сразу в релиз. «А как же тесты?» — воскликнет какой-нибудь дотошный коллега. «А зачем?» — ответите вы в один голос с менеджером. Зачем нам писать тесты? Как объяснять их необходимость другим? Зачем вовлекать тестировщиков, аналитиков и других участников? В этом посте я расскажу, как объяснить пользу тестов любому участнику проекта, а также зачем стоит тесты автоматизировать. И подкрепим все это серьезными исследованиями.
Нужна ли вам автоматизация? Начнем рассуждение от обратного.
Почему тесты писать не надо?
1. Это дорого
Программист — один из самых дорогих участников проекта, и его работа очень сильно влияет на бюджет. И вообще, написание тестов — не барское дело. Можно просто нанять студентов за копейки, чтобы отдать им и написание этих тестов, и тестирование программ. Пусть тыкают в дисплеи, нажимают на клавиши — все будет хорошо.
2. Это нудно
Разработка автотестов — это очень нудная работа. Ее можно поручить кому угодно. Я 5 лет учился программировать в универе, и еще два года в аспирантуре. Я хочу проектировать классную архитектуру, применять крутые паттерны и запускать ракеты на Марс. А тесты — это задача не моего уровня.
3. Это бесполезно
Зачем вообще писать тесты, если мой код работает? Каждая его строчка прошла через мой внутренний гений. Как оно вообще может создавать проблемы в приложении? Это исключено, я каждый байтик насквозь вижу.
4. Это «не мои проблемы»
Сегодня я в одной команде фичу написал, завтра пойду в другую команду новую фичу делать. Что здесь дальше будет, разберутся без меня, даже если и найдется какая-то ошибка.
Почему весь этот список — неправда?
Теперь пройдемся по всем пунктам и опровергнем каждый. Заодно приведем еще несколько интересных фактов из исследований известных компаний вроде Microsoft (On the Effectiveness of Unit Test Automation at Microsoft за авторством Laurie Williams, Gunnar Kudrjavets, Nachiappan Nagappan), а также менее известной компании из Южной Америки.
1. В масштабах проекта это недорого
Обратимся к «Совершенному коду» Стива Макконнелла. Если оценить, как со временем изменяются трудозатраты, то можно заметить, что постепенно доля тестирования, выполняемого разработчиками, сокращается с 25% до 8–10%. Больше ресурсов начинают красть другие активности.
Есть другое исследование, где Microsoft изучил примерно 30 внутренних команд разного размера — по 28–35 разработчиков и 14–20 тестировщиков в каждой.
Это исследование показывает примерно те же цифры. При внедрении практик автоматизированного тестирования время на разработку выросло в среднем на 26%. Но разработка — это не вся проектная активность, есть еще конструирование, интеграция, внедрение и так далее. Поэтому рост на четверть только одного из множества процессов — небольшая потеря для бюджета, особенно в свете падения затрат на ручное тестирование.
2. Это гораздо веселее, чем проблемы в продакшене
Возможно, написание тестов несколько скучновато — тестовый код тривиален и редко таит в себе интересные задачи. Но вашему клиенту точно не нужен вечный пожар, который вы будете тушить, находя дефекты во время внедрения или бета-тестирования.
3. Это полезно для всех
Чем меньше веришь в эталонность своего кода, тем в итоге будет лучше как команде, так и продукту. А в первую очередь вам самим — научитесь на своих ошибках и при этом без неприятных последствий.
4. Это станет вашей проблемой
Если написать код, а потом забросить его поддержку, то через месяц-другой можно запросто пролететь мимо релиза. В рамках общего тестирования продукта будет найдено множество дефектов — в итоге до продакшна не дойдет и ваша фича, и еще несколько фич коллег.
Тестирование с точки зрения ролей в команде
Переходим в наступление. Посмотрим на тесты глазами всех участников процесса разработки: клиента, владельца продукта, тестировщика и разработчика.
Клиент
Что бывает, когда не пишут тесты? Например, ты теряешь спутник, который застрахован на 2,6 млрд рублей.
28 ноября Роскосмос с помощью ракеты «Союз-2.1 б» запустил спутник «Метеор-М» № 2–1. Он не долетел — немного отклонился от курса и упал в океан. Разработчики ПО, которое отвечало за корректировку маршрута, забыли указать актуальные данные. Планировалось, что спутник будет выводиться на орбиту с космодрома «Байконур», а запуск состоялся в нескольких десятках тысяч километров от него — с космодрома «Восточный». Один маленький тест для проверки входных данных — и 2,6 млрд рублей были бы спасены.
В интернете можно найти множество историй о том, как одна маленькая ошибка в ПО может повлечь миллиардные расходы. Большинство связаны с медициной и космосом, есть много примеров и в банковской отрасли. Например, в 2012 году в сети одного крупного западного банка накатили на все сервера одно и то же обновление. После этого сервера со старым кодом начали генерировать дополнительные отчеты, потому что на упреждающем фронте исчезли нужные блокировки. В итоге была впустую сгенерирована отчетность на 440 млн долларов.
С другой стороны, что бывает, если код продукта полностью закрывается тестами? С ним можно смело делать то, что никто не делал до тебя. И это очень классно.
Владелец продукта
Важная характеристика продукта с будущим — это возможность его расширения и долгосрочной поддержки. На английском это называется красивым словом Maintainability.
Это значит, что продукт, который развивается несколько лет (или даже несколько десятков лет), постоянно обеспечивает какой-то минимальный уровень качества, и количество дефектов в нем не превышает некоторого максимума. Мы не должны делать хуже пользовательскому опыту. Мы не должны существенно ухудшать качество наших фич. Это можно гарантировать с помощью тестов, ручных и автоматизированных.
В другом исследовании Microsoft можно подсмотреть еще одну интересную цифру. В рамках команды из 32 разработчиков количество дефектов, которое доходило до пользователя, уменьшилось на 20,9% — после того, как во второй версии продукта команды разработки стали применять автоматизацию тестирования и писать модульные тесты. При этом вторая версия обзавелась дополнительными фичами, выросла по объему кода примерно на 20% и еще на 20% была переписана. Да и сами тестировщики стали замечать, что качество их ПО стало выше — стало интереснее работать.
В IBM тоже проводили подобные исследования и в итоге выяснили, что стоимость устранения бага, обнаруженного после релиза, выше в 4–5 раз чем при обнаружении во время проектирования или реализации. Автотесты, если их регулярно запускать, помогают вовремя сигнализировать о том, что есть какие-то проблемы. Они помогают резать эти дополнительные косты в бюджете. В итоге стоимость поддержки ПО станет ниже —, а качество пользовательского опыта при этом вырастет.
Тестировщик
Здесь вопрос уже немного в другом: зачем нужна автоматизация тестирования? На помощь снова приходит исследование Microsoft. Согласно ему, после автоматизации тестирования тестировщикам стало гораздо сложнее искать баги. Но при тех же временных затратах они теперь находят гораздо больше сложных багов, которые раньше с большей вероятностью просачивались в продакшн. Одновременно с этим стало гораздо меньше тривиальных ошибок, поскольку их убирают еще до тестировщиков.
Вот отчет по ошибкам в рамках одного внутреннего проекта Microsoft:
Первая версия — до внедрения автоматического тестирования, вторая — после. Ошибки разделены на четыре категории. Чем выше категория, тем сложнее ошибки. Суммарно во второй версии количество багов уменьшилось на 21%. Количество багов из простых категорий (3 и 4) уменьшилось существенно. Доля более сложных багов осталась такой же либо чуть-чуть подросла из-за того, что общее количество багов снизилось.
Теперь тестировщики могут писать более сложные тест-кейсы, которые максимально приближены к пользовательскому опыту. Самые простые ошибки программист с большой вероятностью устранит сам, если он будет систематически дополнять свой код каким-то тестовым.
Почему до тестировщиков стало доходить меньше багов? Есть известная многим пирамида, которая делит различные виды тестирования по слоям. Эта пирамида говорит о том, что самых примитивных модульных тестов, которые затрагивают исключительно маленькую часть ПО, должно быть в проекте больше всего. А сложных end-to-end или ручных тестов должно быть минимальное количество, но при этом они должны искать самые сложные баги — это так называемые мигающие, плавающие или серые тесты. Если эту пирамиду перевернуть, появляется так называемая воронка тестирования, в которую сыплются всевозможные баги.
Источник: https://twitter.com/noahsussman
На каждом уровне этой воронки баги отфильтровываются. Чем сложнее и интересней баг, тем ниже он опускается в этой воронке. Большое количество модульных тестов устраняет большинство багов до этапа ручного тестирования. Поэтому время самого ручного тестирования сокращается, а к пользователю могут проникнуть только баги, связанные с какой-то специфической функциональностью. 90–95% с ними никогда в жизни не столкнутся.
Разработчик
Для разработчика я выделил целых пять преимуществ, которые ему дают тесты:
- Понимание требований
При написании теста разработчик начинает лучше понимать требования, благодаря коммуникации с нетехническими специалистами. Допустим, нужно реализовать какое-то поле ввода. Когда вы пишите тесты к своему коду, то начинаете задавать вопросы:, а что если я сюда подставлю пустое значение? А что если я введу 150 символов вместо 10 разрешенных? Уточняя эти моменты, вы делаете более стабильным свой код и избавляете его от самых простых ошибок.
- Стабильность при рефакторинге
Тесты позволяют нам гарантировать стабильность нашего приложения при рефакторинге, особенно при жирном рефакторинге. Предположим, у вас есть какой-то старый модуль — огромный легаси-код, который многие бояться трогать, потому что непонятно, что там происходит. Если эту штуку закрыть тестами, они качественно проверят, что основные кейсы у нас работают. Не так страшно будет заходить и рефакторить это все под капотом, менять на какие-то свои новые реализации. Мы просто будем подтверждать, что те изменения, которые появились в нашем приложении, ничего не сломали.
- Формирование документации
Тесты служат отличной документацией к вашему коду. Гораздо легче понять чужой код, когда есть какой-то потребитель этого кода. И самым первым потребителем как раз выступают тесты. Не заходите в Jira, не изучайте объемные комментарии в Confluence — вы можете просто открыть тестовый класс, набор каких-то тестовых методов к вашему коду, и понять, что этот код делает, просто почитав названия тестовых методов и поняв, какие данные там передаются.
- Улучшение API
Если мы используем итеративные практики написания тестов, типа TDD, или заранее планируем как будем охватывать тестами какую-то конкретную сущность в нашей программе, это позволит сделать лучше ее интерфейс. Мы начинаем продумывать какие данные будем продавливать в эту сущность, какие данные она будет нам возвращать. Это позволит сразу написать качественный интерфейс вместо того, чтобы потом постоянно добавлять новые контексты и параметры.
- Уменьшение количества ошибок
Понятно, что тесты уменьшают число ошибок в коде. Меньше ошибок — больше свободного времени. Вот что сами разработчики говорят про написание тестов (в данном случае модульных) в исследовании Microsoft:
Подведем итоги
Написание тестов должно поддерживаться со стороны менеджмента. Если вы сами решите, что будете писать тесты, а остальные при этом не пишут, то инициатива, скорее всего, через пару месяцев заглохнет. Менеджмент должен поддерживать написание тестов и понимать, зачем они вообще появляются в проекте.
Конечно, с тестами разработка становится дольше — в среднем на 26%. В зависимости от сложности языка программирования, фичей и опыта программиста, эта доля может уменьшиться до 8% или подскочить до 40–50% времени. Если время выхода на рынок для вас максимально критично — отложите написание тестов, выпустите MVP и возвращайтесь к тестам потом.
На мой взгляд, писать тесты внутри команды должны не только разработчики, но и тестировщики. Доступ к коду должен быть у всех участников команды, и все они должны иметь возможность влиять на изменения кода. Тестировщики могут предложить больше исключительных ситуаций и разнообразных edge-кейсов, чем среднестатистические разработчики. Соответственно, качество тестов и охват улучшатся.
Надеюсь, все приведенные аргументы помогут вам объяснить внутри команд, а заодно самому себе, зачем нужны тесты, зачем стоит писать их каждый день и как они делают код лучше.