Как мы автоматизировали мутационное тестирование unit тестов на проекте в крупном банке с использованием Stryker.NET
Качественные модульные тесты помогают оптимизировать ресурсы команды разработки и увеличивают надежность создаваемого продукта. Оценить эффективность этих тестов можно с помощью автоматизированного инструмента для мутационного тестирования Stryker.NET.
227 открытий2К показов
Качественные модульные тесты помогают оптимизировать ресурсы команды разработки и увеличивают надежность создаваемого продукта. Оценить эффективность этих тестов можно с помощью автоматизированного инструмента для мутационного тестирования Stryker.NET.
Меня зовут Юрий Каган, я Software/System Architect в IT_ONE. Расскажу об опыте применения Stryker.NET на проекте в крупном банке.
Качественные юнит-тесты — основа надежного ПО
Несмотря на то, что юнит-тесты — только один из компонентов пирамиды тестирования ПО, они — база для создания качественного кода:
– Благодаря проверке отдельных фрагментов кода можно выявить дефекты на ранних стадиях, не пропуская их на следующие этапы разработки. Известно, что чем раньше обнаружена ошибка, тем ниже стоимость её исправления.
– Модульные тесты обеспечивают стабильность разработки, служа некой страховкой: они предотвращают регрессии при изменениях и рефакторинге.
– Проведённые тесты служат документацией и ускоряют внедрение нового функционала. По ним можно понять изначальный замысел автора кода и упростить разработку.
Для оценки качества юнит-тестов разработчики обычно пользуются метрикой Code Coverage, отражающей процент покрытия тестами исходного кода. Такая информация может собираться различными способами в зависимости от типа используемого инструмента, но результат получается схожий: данные о том, какие конкретно фрагменты кода выполнялись во время тестов. О каких бы то ни было проверках речи не идет. То есть, по нашему опыту, технически очень легко можно написать тесты, которые будут давать 100% Code Coverage и при этом ничего не проверять. Это означает, что даже высокий процент покрытия кода тестами не гарантирует, что они разработаны качественно и поведение вашего кода надёжно зафиксировано. После них нельзя исключать скрытые ошибки.
Как следствие, постоянно осознавая риски некачественных тестов, разработчики могут начать бояться рефакторинга — ведь любые изменения потенциально грозят дефектами в коде. Причем допущенный дефект может быть обнаружен намного позже, уже в продакшене. Всё это приводит к дополнительным рискам, замедляет и удорожает цикл разработки.
Но существует и другая, не столь широко известная метрика, которая более объективно описывает надёжность проведённых тестов: она определяется в процессе мутационного тестирования. Первые упоминания об этом подходе мы встречаем еще в 1970-х годах. Тогда он уже признавался перспективным, но в то же время — практически неприменимым из-за запредельного объема работы. Сегодня мы имеем возможность автоматизировать большую часть этой работы с помощью инструментов, например, Stryker.NET.
Принципы мутационного тестирования
Алгоритм мутационного тестирования достаточно прост: в исходный код (базу) вносятся различные изменения (мутации). Существует несколько разновидностей мутаций. Основные из них:
– изменение операторов: замена арифметических и логических операторов (например, + на -, >= на >),
– изменение значений: замена булевых значений, удаление вызовов методов или изменение литералов,
– изменение условий: в if, циклах и логических выражениях.
Затем на этом модифицированном коде выполняются модульные тесты для проверки их чувствительности к изменениям. Мутанты, которые вызывают провал тестов, считаются «убитыми» (Killed Mutants), остальные — «выжившими». По итогу проверки формируется отчет, где ключевая метрика качества тестов — Mutation Score — доля убитых мутантов от их общего количества. Если тесты не «убивают» подавляющее число мутантов, значит их нельзя считать достаточно эффективными.
Среди инструментов для автоматизации проведения мутационного тестирования мы остановили выбор на Stryker.NET и вот, почему:
– на данный момент Stryker.NET обладает наибольшим объемом автоматизированных операций: он самостоятельно вносит мутации в исходный код, запускает юнит-тесты и генерирует подробные отчеты;
– Stryker.NET полностью интегрирован с .NET-экосистемой: поддерживает .NET и .NET Framework, основные тестовые фреймворки (xUnit, NUnit, MSTest);
– Stryker.NET – это бесплатный инструмент с открытым исходным кодом, постоянно обновляемый и поддерживаемый сообществом, что гарантирует его актуальность и развитие.
По нашей практике, Stryker.NET будет полезен для трех категорий пользователей:
– Разработчик может убедиться, что новый код покрыт качественными тестами и изменения не привели к деградации существующих тестов, а также находить и удалять тесты, которые ничего не тестируют и только отнимают ресурсы.
– Ревьюер может быстро и надежно проверить качество тестов в pull request.
– ИТ-архитектор и руководитель разработки могут регулярно строить и анализировать отчеты, чтобы мониторить «здоровье» юнит-тестов во всей кодовой базе.
Установка и использование Stryker.NET
Технически Stryker.NET представляет из себя dotnet tool. Соответственно, чтобы его установить, необходимо выполнить команду в cmd или в PowerShell консоли:
dotnet tool install -g dotnet stryker
Получение и анализ отчетов: результаты выводятся в консоль и сохраняются в подробном HTML-отчете.
Stryker.NET поддерживает несколько сценариев, простейший из них — анализ проекта с кодом. В этом случае необходимо запустить мутационное тестирование из папки, где расположен файл проекта, указать имя этого файла без пути и через ключ tp указать полные пути ко всем файлам проектов, содержащим тесты для проекта с кодом. Например:
dotnet stryker -p “project.csproj” -tp “c:\git\project.tests\project.tests.csproj”
В результате тестирования программа сгенерирует отчет, где в первой колонке будет выведена метрика Mutation Score в целом по проекту и по отдельным файлам, причем разделённая на две группы: Of total — общее количество мутантов, Of covered — количество мутантов, находящихся в тех фрагментах кода, для которых вообще существуют тесты.
В колонке Killed отображается количество убитых мутантов, в колонке Survived — количество выживших. В колонке Timeout — количество мутантов, которые привели к зацикливанию выполнения тестов и их прерыванию утилитой. Значения остальных колонок, а также другую важную информацию можно посмотреть в документации на официальном сайте https://stryker-mutator.io/docs/stryker-net/introduction/.
Также важно отметить, что отчёт доступен для более глубокого и детализированного анализа: можно раскрыть параметры каждого файла и увидеть подробную информацию о том, какие именно изменения были произведены, какие из мутантов выжили и почему.
Stryker.NET умеет генерировать отчеты не только в HTML, но и в других форматах: например, в JSON, который очень удобен для автоматического анализа, если разработчики планируют встроить этот инструмент в свой CI/CD-пайплайн. Кроме того, есть встроенный дашборд, который, к сожалению, невозможно развернуть локально — он доступен только как online сервис (https://dashboard.stryker-mutator.io).
Применение Stryker.NET при разработке банковских продуктов
Мы внедрили использование Stryker.NET в проекте для крупного банка, чтобы с помощью этого инструмента решить ряд взаимосвязанных задач. Самая главная проблема заключалась в том, что мы хотели улучшить качество тестов посредством мутационного тестирования, но его проведение вручную — предельно утомительная и требующая много времени процедура. Ни Product Owners, ни разработчики не были готовы постоянно выделять на это ресурсы.
Без регулярного мутационного тестирования, нам было практически невозможно оценить текущее состояние всей кодовой базы с точки зрения качества unit тестов. У нас было много unit тестов, мы имели высокий процент Code Coverage, но не знали, насколько эти тесты нас защищают. В свою очередь, без понимания текущего состояния у нас не было возможности устанавливать команде цели по улучшению ситуации.
На данный момент команда активно использует Stryker.NET на всех этапах разработки.
Мы столкнулись и с ограничением использования Stryker.NET: попытка его интеграции в CI/CD-пайплайн оказалась неудачной. Это связано с тем, что кодовая база проекта насчитывает несколько миллионов строк, и выполнение всех проверок занимает примерно 12 часов. Однако мы думаем, что на проектах меньшего размера такая интеграция должна сработать.
Мы выбрали альтернативный вариант: был внедрен регулярный автоматический пост-релизный прогон Stryker.NET по ветке master, в результате которого генерируется сводный отчет. Проводится анализ изменений сводного отчета по всем проектам от релиза к релизу. По данным каждого сводного отчета мы можем оценивать работу конкретных проектных команд, и если их метрика Mutation Score недостаточно высока, – ставить цели по улучшению.
Резюме
Итак, мутационное тестирование – мощный инструмент для повышения качества юнит-тестов и, соответственно, качества кода. Оно позволяет не только узнать, какой процент кода покрыт тестами, но и убедиться в том, что эти тесты действительно защищают код от ошибок.
Внедрение Stryker.NET — шаг к более надёжной и предсказуемой разработке. Его регулярное использование помогает уверенно вносить изменения, рефакторить код и добавлять новый функционал.
Мы рекомендуем начать использование Stryker.NET в ваших проектах с ключевых модулей. При этом стоит постоянно делиться опытом с командой, чтобы наиболее эффективно улучшать программный продукт.
227 открытий2К показов









