Большинство разработчиков признают ценность модульных тестов, даже если им не всегда нравится их писать. Они задействуют их в новых разработках или при разработке через тестирование (test-driven development, TDD), но их применение к унаследованному коду явно недооценено, пишет на портале TechBeacon старший инженер Steampunk по DevSecOps Джин Готимер.

Многие разработчики отказываются от добавления модульных тестов в унаследованный код, потому что считают, что делать это уже поздно. На самом деле это неправильно, так как модульные тесты на самом деле приносят пользу при разработке новых программных систем в сочетании с унаследованным кодом (brownfield development), превращая его в надежный «боевой» код. Они позволяют выполнять плановое обслуживание в обычном режиме, устраняя риск поломки системы из-за вносимых в нее изменений.

Модульные тесты документируют, что должен делать код по мнению разработчиков. Конечно, действительность не всегда совпадает с декларируемыми намерениями, особенно это проявляется через несколько недель, месяцев или лет после написания приложения. Но это не значит, что не надо документировать текущее состояние дел.

Ниже приводится четыре причины добавить модульные тесты в существующий код без излишнего риска и усилий.

1. Устранение неполадок в коде

Устранение неполадок предполагает чтение исходного кода, наблюдение за тем, как обрабатываются входные данные, отслеживание операторов if/then/else и другие проверочные процедуры, которые длятся до тех пор, пока не будут получены результаты. Этой работы не избежать, но вы можете записать результаты для следующего разработчика. Добавьте модульные тесты и затем запустите их, чтобы узнать, как код поведет себя. Если вы не можете отследить поведение кода или просто не хотите этого делать, вы можете использовать модульный тест в качестве эксперимента. Задайте результирующие параметры, запустите модульный тест, провалите его и затем обновите, чтобы он отражал реальность. Добавление модульных тестов становится не вспомогательной задачей, а инструментом в арсенале поиска и устранения неполадок в коде.

2. Обучение новых разработчиков

Обучение новичка работе с базой кода может быть очень похоже на поиск и устранение неисправностей. Пройтись по исходному коду, отследить условные пути, определить крайние случаи и описать обработку ошибок — это действия общего характера, которые требуются и вам, и ему. При обучении работе с кодовой базой зафиксируйте свое понимание поведения кода в виде модульных тестов. Еще лучше, если по мере объяснения вами работы кода новые разработчики сами будут писать модульные тесты. Это даст им возможность внести свой вклад в код и сделает их активными участниками обучения. Задавая уточняющие вопросы, они смогут понять код гораздо лучше, чем если бы они просто слушали лекцию.

3. Обновление библиотек

Обновление библиотек — это бесконечная задача по обслуживанию современных программных проектов. Фреймворки, утилиты и даже сами языки программирования меняются так часто, что целые релизы могут быть посвящены только обновлению и тестированию. Поскольку обновления библиотек рискованны, повторяются и, как правило, практически ничего не добавляют пользователям, они часто откладывается на потом, пока проект не будет вынужден обновиться. Это порочный круг: вы хотите быть уверены, что обновление ничего не сломает, но вероятность этого возрастает тем больше, чем больше времени проходит между обновлениями. Написание тестов для разработчиков, включающих вызовы из вашего кода к библиотекам и фреймворкам — это удобная система безопасности, позволяющая поддерживать согласованное поведение приложения во время обновления.

Для пуристов это определение «модульного теста» будет слишком широким, но это все равно могут быть быстрые тесты, использующие ваш фреймворк модульного тестирования для документирования поведения кода.

Почему бы не задокументировать поведение и не облегчить следующее обновление? Если это уменьшит инертность нового незначительного обновления и последующих, то это будет выигрыш для всех. Больше инкрементных обновлений означает меньший риск в каждом из них. Вы можете быть проактивными и с большей вероятностью получить последние исправления.

Еще один выигрышный ход — документировать поведение библиотек, на которые вы полагаетесь при взаимодействии с кодом, чтобы избавить себя от необходимости каждый раз читать журналы для поиска изменений. Создайте хороший набор тестов, и вы сразу же увидите влияние обновлений в контексте вашего собственного кода.

4. Изменение поведения кода

Модульные тесты — отличный способ убедиться, что поведение кода не изменится, но что если нужно изменить его поведение? В конце концов, даже унаследованный код время от времени получает новую функциональность. Опять же, модульные тесты — верный способ понять поведение унаследованного кода, а это значит, что они являются первым шагом на пути к его изменению. Напишите тесты, продемонстрируйте текущее поведение и измените их, чтобы отразить желаемое поведение. Затем код можно изменить таким образом, чтобы он проходил новые тесты.

Это TDD на существующем коде. Такой подход заставляет обсуждать изменения заранее, пока тесты модифицируются, и это удерживает вас от попадания в лабиринт сомнений и расширения границ проекта.

Наведите порядок с помощью модульных тестов

«Всегда оставляйте лагерь чище, чем вы его нашли». Этот девиз бойскаутов был принят в индустрии ПО в качестве лучшей практики сопровождения. Всякий раз, когда вы редактируете код, очищайте его. Независимо от того, рефакторинг ли это или модернизация, проводите модульное тестирование. Вне зависимости от того, копаетесь ли вы в исходном коде для устранения неполадок, обучения нового разработчика принципам работы, выполнения обновлений или добавления новых функций, уделите время добавлению модульных тестов. Вы очистите лагерь для тех, кто придет после вас, а этим кем-то можете оказаться вы сами, причем в тот день, когда у вас не будет времени или терпения разбираться в запутанном коде.