Тестирование мутаций с помощью PITest

1. Обзор

Тестирование программного обеспечения относится к методам, используемым для оценки функциональности программного приложения. В этой статье мы собираемся обсудить некоторые метрики, используемые в индустрии тестирования программного обеспечения, такие как охват кода и тестирование мутаций , с особым интересом о том, как выполнить тест на мутацию с использованием http://pitest .org/[ Библиотека PITest ].

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

2. Зависимости Maven

Как вы можете видеть в конфигурации зависимостей Maven, мы будем использовать JUnit для запуска наших тестов и библиотеку PITest для введения мутантов в наш код - не волнуйтесь, через секунду мы увидим, что такое мутант.

Вы всегда можете посмотреть последнюю версию зависимостей в центральном репозитории maven, следуя этой link .

<dependency>
    <groupId>org.pitest</groupId>
    <artifactId>pitest-parent</artifactId>
    <version>1.1.10</version>
    <type>pom</type>
</dependency>

Для того чтобы библиотека PITest была запущена и работала, нам также необходимо включить плагин pitest-maven в наш файл конфигурации pom.xml :

<plugin>
    <groupId>org.pitest</groupId>
    <artifactId>pitest-maven</artifactId>
    <version>1.1.10</version>
    <configuration>
        <targetClasses>
            <param>com.baeldung.testing.mutation.** </param>
        </targetClasses>
        <targetTests>
            <param>com.baeldung.mutation.test.** </param>
    </targetTests>
     </configuration>
</plugin>

3. Настройка проекта

Теперь, когда мы настроили наши зависимости Maven, давайте посмотрим на эту понятную функцию палиндрома:

public boolean isPalindrome(String inputString) {
    if (inputString.length() == 0) {
        return true;
    } else {
        char firstChar = inputString.charAt(0);
        char lastChar = inputString.charAt(inputString.length() - 1);
        String mid = inputString.substring(1, inputString.length() - 1);
        return (firstChar == lastChar) && isPalindrome(mid);
    }
}

Все, что нам сейчас нужно, это простой тест JUnit, чтобы убедиться, что наша реализация работает желаемым образом:

@Test
public void whenPalindrom__thenAccept() {
    Palindrome palindromeTester = new Palindrome();
    assertTrue(palindromeTester.isPalindrome("noon"));
}

Пока все хорошо, мы готовы успешно запустить наш тестовый пример как тест JUnit.

Далее в этой статье мы сосредоточимся на покрытии кода и мутаций с использованием библиотеки PITest.

4. Покрытие кода

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

Мы можем измерить эффективное покрытие кода на основе путей выполнения, используя такие инструменты, как Eclemma , доступные в Eclipse IDE

После запуска TestPalindrome с покрытием кода мы можем легко достичь 100% оценки покрытия. Обратите внимание, что isPalindrome является рекурсивным, поэтому совершенно очевидно, что проверка длины пустого ввода в любом случае будет выполнена.

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

5. Покрытие мутации

Мутационное тестирование - это метод тестирования, используемый для улучшения адекватности тестов и выявления дефектов в коде. Идея состоит в том, чтобы динамически изменять производственный код и вызывать сбои тестов.

Хорошие тесты не пройдут

Каждое изменение в коде называется мутант , и это приводит к измененной версии программы, называемой мутация .

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

Теперь давайте запустим тест с использованием Maven с параметром цели, установленным на:

org.pitest: pitest-Maven: mutationCoverage .

Мы можем проверить отчеты в формате HTML в каталоге target/pit-test/YYYYMMDDHHMI :

  • 100% покрытие линии: 7/7

  • 63% мутации: 5/8

Ясно, что наш тест охватывает все пути выполнения, таким образом, оценка покрытия линии составляет 100%. С другой стороны, библиотека PITest представила 8 мутантов , 5 из них были убиты - вызвано неудачей - но 3 выжили.

Мы можем проверить отчет com.baeldung.testing.mutation/Palindrome.java.html для более подробной информации о созданных мутантах:

ссылка:/uploads/mutations.png%20715w[]



Это мутаторы, активные по умолчанию при запуске теста покрытия мутацией:

  • INCREMENTS MUTATOR__

  • VOID METHOD CALL MUTATOR__

  • RETURN VALS MUTATOR

  • MATH MUTATOR__

  • NEGATE CONDITIONALS MUTATOR

  • INVERT NEGS MUTATOR

  • CONDITIONALS BOUNDARY MUTATOR

Для получения более подробной информации о мутаторах PITest вы можете проверить официальную ссылку страница документации .

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

6. Улучшение показателя мутации

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

Давайте возьмем первую мутацию - отрицательную условную - в строке 6 в качестве примера. Мутант выжил, потому что даже если мы изменим фрагмент кода:

if (inputString.length() == 0) {
    return true;
}

Для того, чтобы:

if (inputString.length() != 0) {
    return true;
}

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

@Test
public void whenNotPalindrom__thanReject() {
    Palindrome palindromeTester = new Palindrome();
    assertFalse(palindromeTester.isPalindrome("box"));
}
@Test
public void whenNearPalindrom__thanReject() {
    Palindrome palindromeTester = new Palindrome();
    assertFalse(palindromeTester.isPalindrome("neon"));
}

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

  • 100% покрытие линии: 7/7

  • 100% охват мутации: 8/8

7. Конфигурация тестов PITest

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

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

<configuration>
    <targetClasses>
        <param>com.baeldung.testing.mutation.** </param>
    </targetClasses>
    <targetTests>
        <param>com.baeldung.mutation.test.** </param>
    </targetTests>
    <mutators>
        <mutator>CONSTRUCTOR__CALLS</mutator>
        <mutator>VOID__METHOD__CALLS</mutator>
        <mutator>RETURN__VALS</mutator>
        <mutator>NON__VOID__METHOD__CALLS</mutator>
    </mutators>
</configuration>

Более того, библиотека PITest предлагает множество опций, доступных для настройки ваших стратегий тестирования , вы можете указать максимальное количество мутантов, представленных классом, используя, например, параметр maxMutationsPerClass . Подробнее о параметрах PITest можно узнать в официальном руководстве по быстрому запуску Maven .

8. Заключение

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

Мы также увидели, как анализировать базовые отчеты PITest, улучшая показатель охвата мутациями .

Несмотря на то, что мутационное тестирование выявляет дефекты в коде, его следует использовать с умом, потому что это чрезвычайно дорогой и длительный процесс .

Вы можете ознакомиться с примерами, представленными в этой статье, в связанном GitHub проекте .