В прошлом своем посте я описал обобщенный подход к TDD. Возможно, многие кто познакомился с описательной частью сделал для себя ряд выводов о ценности данного подхода. Скорее всего это разработчики, которые уже начинали создавать тесты.. получать первые красные результаты, исправлять ошибки и видеть красивые зеленые полоски, однако делали это не систематично, без соблюдения каких либо правил.
В этой стаье, я хочу рассказать о том, какие все таки преимущества несет за собой TDD, оглядываясь на тот опыт, который был накоплен мной за годы работы. По моему мнению, основные преимущества следующие (самые главные я упомянул ранее):
- Превращение гипотезы в модель.
- Устойчивость к изменениям и регрессии.
- Сокращение времени на обнаружение ошибок и отладку.
- Улучшение качества кода.
- Ускорение процесса разработки.
Превращение гипотезы в модель.
Используя принцип "Сначала тест", вы строите предположения относительно проектируемой вами сущности. Эти предположения, выражаются в виде набора тестов (предположения могут выдвигаться и по одному, соотвественно, создавая один тест и добавляя их в процессе расширения). Согласно принципу бритвы Окама создается минимальное количество предположений, способное объяснить ту или иную гипотезу. Далее итерируя в цикле TDD, вы приходите к реализации, тем самым пройдя путь от гипотезы к модели.
Устойчивость к изменениям и регрессии.
Так как весь программный код создается одновременно с тестами (точнее, тесты создаются сначала), мы получаем довольно прочный фундамент на котором стоит создаваемая система. Обширный и качественный набор юнит тестов и интеграционных тестов является надежным щитом, как и для случайных изменений (внесенных ошибочно), так и явных изменений, которые могут косвенно влиять на работоспособность других компонентов системы. Это может не так остро ощущаться на ранних этапах конструирования, так как существует еще не большое количество связанных сущностей, логика реализации, как правило, довольно понятна, что уменьшает вероятность внесения случайных ошибок. Внесение изменений на поздних этапах конструирования, или после выпуска продукта, на этапе поддержки, представляет собой более серьезную проблему. Если система уровня корпоративного приложения, мы имеем: сложную бизнес логику, большое количество зависящих друг от друга компонент, набор различных конфигураций для приложения. Любое, даже самое минимальное изменение представляет собой потенциальную опасность. При отсутствии автоматических тестов, разработчик внося изменение проверяет его на каком-то ограниченном сценарии использования, при этом косвенное влияние изменения на другие компоненты остаеться для него не замеченным.. Такого рода ошибки, огромная головная боль. Тестировщик находит (если повезет, если нет, находит заказчик), такую ошибку, поведение которой и близко не соотвествует тому изменению, которое было внесено.. создает тест репорт, который попадает в руки менеджеру, который в свою очередь может отдать этот дефект другому разработчику.. ну а тот проведет 2 дня отлаживая и находит изменение, которое сделано совсем не им, и даже не в зоне его ответственности. При этом проект уже может находиться в стрессой ситуации. В команде паника, и желание покинуть скорее этот проект. При наличии качественного набора тестов, вероятность такого поворота событий значительно снижается. Я не могу сказать о том, что любой неверный (или неожиданный) шаг может быть выявлен тестами. Если тесты проходят, это не означает, что ошибок нет, это означает, что тесты их не выявили. Однако, опыт показывает, что проект разработанный про правилам TDD, более устойчив. Мне приходилось работать над крупной системой, с очень хорошим покрытием кода тестами. И не один раз случалось так, что работая над какой либо проблемой и внося изменения, я получал фейлы тестов, которые зависят от работоспособности того класса, который я менял.. При этом я понятия не имел, о такой связи. Изменения, которые были внесены, были неожиданны, для других компонент - как правило, я либо пересматривал те изменения, которые внес, либо изменял зависящую компоненту, чтобы она сохранила свою работоспособность. Интеграционные тесты, создают своего рода tracebility, для всего кода. Тоже самое касается и регрессии. TDD накладывает правила и на багфиксинг - согласно этому правилу любой дефект исправляется следующим образом: создается тест, доказывающий существование этого дефекта, дефект исправляется, код и тест заливаются в систему контроля версий. Следуя такой практике, мы гарантируем отсутствие этого дефекта в будущем и как следствие регрессионному проявлению данного дефекта.
Сокращение времени на обнаружение ошибок и отладку.
Предполагаю, что утверждение о сокращение времени на обнаружение довольно очевидно.. При условии, что ошибку обнаруживает тест, это самое короткое время: внес изменения, перезапустил все тесты.. если порядок, заливаем, нет, тогда разбираемся, что не так. Как было сказано выше, при обнаружении "неожиданных" ошибок (т.е. те, которые не выявленные тестами), сначала создается тест их выявляющий, потом сам фикс. Как правило, это процесс гораздо более быстрый, нежели классический поиск ошибок с отладчиком. Конечно, при начальном исследовании все таки придется, запустить приложение под отладчиком, пройти долгий путь, минуя окна и диалоги, заполняя всякие поля, пока не дойдя в место с дефектом. Но после того как понятно какой из классов содержит дефект, необходимо лишь добавить тест к существующему тест-набору, и запускать тест, что будет занимать на порядок меньше времени. С TDD тест заменяет отладчик.
Улучшение качества кода.
Для разработчиков не пишущих тесты это явно удивительное утверждение. Как тестирование может влиять на качество кода? А на самом деле, речь идет о следующем: разрабатывая тест сначала, вы волей неволей, становитесь пользователем того кода, который разрабатываете, соотвественно ваше мышление становится мышлением пользователя (пользователя, вмысле другого программиста, использующего код). Это ведет к тому, что разрабатываемые сущности более строго соответствуют принципу одиночной ответственности, интерфейсы более ограненные, не содержат ничего лишнего, а лишь то, что в точности необходимо клиенту. К этому стоит добавить то, что написание теста сначала провоцирует использовать большее количество фабрик и интерфейсов. Методы классов, как правило, не очень большие, позволяемые их качественно тестировать. Ну и самое главное - TDD, это постоянный рефакторинг кода.. постоянное увеличение качества.
Ускорение процесса разработки.
Постоянная работа над качеством, быстрое обнаружение ошибок, сокращение времени на регрессию, более безопасные изменения кода - собственно ведут к тому, что процесс разработки движется более быстро. К тому же, поиск собственных ошибок, их исправление, своевременное и безболезненное, постоянное обучение (а TDD, наверное единственные способ стать более умным программистом), позитивно складывается и на атмосфере в команде.. а хорошо сложенная команда, это залог успеха любого програмного проекта.
