Введение в тестирование мутаций

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

Что такое тестирование мутаций?

Тестирование мутаций основано на простой, но глубокой идее: если в вашем коде есть ошибка, ваши тесты должны её обнаружить. Преднамеренно внося ошибки (мутации) в код, вы можете оценить эффективность ваших тестов при их обнаружении. Мутация может быть такой же простой, как замена переменной, оператора или условия, или такой сложной, как удаление или добавление строки кода. Изменённый код называется «мутантом», а исходный код — «базой».

Вот простой пример, иллюстрирующий эту идею:

# Исходный код
def add(a, b):
    return a + b

# Изменённый код
def add(a, b):
    return a - b

Если ваши тесты для функции add надёжны, они должны завершиться неудачно при выполнении изменённого кода, что указывает на то, что мутация была обнаружена.

Преимущества тестирования мутаций

Увеличение тестового покрытия

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

Улучшение качества тестов

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

Раннее обнаружение ошибок

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

Объективная оценка набора тестов

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

Как работает тестирование мутаций?

Вот пошаговый обзор процесса тестирования мутаций:

  1. Создание мутантов: Операторы мутации применяются к вашему коду для создания мутантов. Эти операторы определяют, как изменить код, например, изменить арифметический оператор или логическое условие.
  2. Выполнение тестов: Ваш набор тестов выполняется для каждого мутанта.
  3. Анализ результатов: Если тест завершается неудачей при мутанте, мутант считается «убитым». Если ни один тест не завершается неудачно, мутант «убегает».
  4. Расчёт ПЭТ: ПЭТ рассчитывается как отношение убитых мутантов ко всем мутантам.

Внедрение тестирования мутаций

Настройка тестирования мутаций

Чтобы начать тестирование мутаций, необходимо выбрать подходящий инструмент. Существует несколько доступных инструментов, таких как Infection для PHP, Stryker для различных языков и Pitest для Java.

Вот пример того, как можно настроить Infection для проекта PHP:

composer require --dev infection/infection
./vendor/bin/infection --show-mutations

Эта команда генерирует мутанты и выполняет ваши тесты против них, предоставляя подробный отчёт о ПЭТ и сбежавших мутантах.

Рекомендации

  • Непрерывная интеграция: Интегрируйте тестирование мутаций в свой конвейер непрерывной интеграции (CI). Это гарантирует, что качество тестов проверяется автоматически при каждом изменении кода.
  • Минимальный порог ПЭТ: Установите минимальный порог ПЭТ для всех запросов на вытягивание, чтобы убедиться, что новые изменения кода не ухудшают качество теста.
  • Сосредоточьтесь на изменённом коде: При добавлении тестирования мутаций к существующей кодовой базе используйте такие опции, как --git-diff-lines, чтобы изменять только те строки, которые были изменены. Это делает процесс более эффективным и целенаправленным.

Пример рабочего процесса

Вот пошаговый пример того, как вы можете реализовать тестирование мутаций в своём рабочем процессе:

  1. Создайте мутантов:

    ./vendor/bin/infection --show-mutations
    
  2. Просмотрите отчёт:

    20 мутаций было создано:
    8 мутантов было убито
    0 мутантов были настроены на игнорирование
    0 мутантов не были покрыты тестами
    12 покрытых мутантов не было обнаружено
    
  3. Определите сбежавших мутантов: Посмотрите на сбежавших мутантов и определите, почему они не были обнаружены вашими тестами.

  4. Улучшите тесты:

    public function testAnnounceWinner($card1, $card2, $expectedWinner): void {
        $war = new War($card1, $card2);
        $this->assertSame($expectedWinner, $war->announceWinner());
    }
    

    Добавьте утверждения или измените тесты, чтобы гарантировать обнаружение сбежавших мутантов.

  5. Повторно запустите тестирование мутаций:

    ./vendor/bin/infection --show-mutations
    
  6. Убедитесь в улучшении:

    20 мутаций было создано:
    20 мутантов было убито
    0 мутантов были настроены на игнорирование
    0 мутантов не были покрыты тестами
    0 покрытых мутантов не было обнаружено