Мутант тест для чего
Mutant TEST 710 мг 90 капсул
Mutant TEST — это средство для повышения уровня тестостерона, которое позволяет тренироваться с непревзойденной силой и увеличить мышечный объем.
И это действует невероятно быстро!
Новая, улучшенная формула MUTANT TEST содержит шесть активных составляющих в тщательно расчитанных дозах. В состав продукта включен мощный комплекс Testosurge, а также цинк, селен, магний и витамин В6, которые обеспечивают оптимальное гормональное равновесие.
Testosurge действует через разные физиологические пути, чтобы по возможности быстрее стимулировать производство тестостерона и поддерживать оптимальный уровень тестостерона, что, в свою очередь, значительно увеличивает мышечную массу и силу. Этот мощный комплекс действует на уровень тестостерона невероятно быстро и эффективно!
Предупреждение
При возникновении любых побочных реакций следует прекратить прием и обратиться к врачу. Хранить в недоступном для детей месте. Хранить при комнатной температуре.
Производитель оставляет за собой право изменять характеристики товара, его внешний вид и комплектность
без предварительного уведомления продавца.
Рекомендации по применению: Принимать 6 капсул натощак, сразу же после пробуждения с 250мл воды.
Рекомендуется использовать ежедневно на протяжении 8 недель, затем сделать перерыв на 4 недели.
Мутационное тестирование
Юнит тесты помогают нам удостовериться, что код работает так, как мы этого хотим. Одной из метрик тестов является процент покрытия строк кода (Line Code Coverage).
Насколько вы уверены в своих тестах? Покрывают ли они все ветки выполнения ваших функций? Тестируют ли они вообще хоть что-нибудь?
Ответ на этот вопрос даёт мутационное тестирование.
Мутационное тестирование — это метод тестирования ПО, основанный на всевозможных изменениях исходного кода и проверке реакции на эти изменения набора автоматических тестов. Если тесты после изменения кода успешно выполняются, значит либо код не покрыт тестами, либо написанные тесты неэффективны. Критерий, определяющий эффективность набора автоматических тестов, называется Mutation Score Indicator (MSI).
Введем некоторые понятия из теории мутационного тестирования:
Для применения этой технологии у нас, очевидно, должен быть исходный код (source code), некоторый набор тестов (для простоты будем говорить о модульных — unit tests).
После этого можно начинать изменять отдельные части исходного кода и смотреть, как реагируют на это тесты.
Одно изменение исходного кода будем называть Мутацией (Mutation). Например, изменение бинарного оператора «+» на бинарный «-» является мутацией кода.
Результатом мутации является Мутант (Mutant) — то есть это новый мутированный исходный код.
Каждая мутация любого оператора в вашем коде (а их сотни) приводит к новому мутанту, для которого должны быть запущены тесты.
Итак, мутационное тестирование создает множество мутантов из вашего кода, для каждого из них запускает тесты и проверяет, выполнились они успешно или нет. Если тесты упали — значит всё хорошо, они отреагировали на изменение в коде и поймали ошибку. Такой мутант считается убитым (Killed mutant). Если тесты выполнились успешно после мутирования — это говорит о том, что либо ваш код не покрыт в этом месте тестами вовсе, либо тесты, покрывающие мутированную строку, неэффективны и в недостаточной степени тестируют данный участок кода. Такой мутант называется выжившим (Survived, Escaped Mutant).
Важно понимать, что мутационное тестирование это не хаотичное преобразование кода, а абсолютно предсказуемый и понятный процесс, который, при наличии одинаковых входных мутационных операторов, всегда выдает один и тот же список мутаций и результирующие метрики на одинаковом тестируемом исходном коде.
Рассмотрим пример. Будем использовать мутационный фреймворк (МФ) для PHP — Infection.
Пусть у нас есть какой-то фильтр, умеющий отфильтровывать коллекцию пользователей по признаку совершеннолетия, написанный в объектно-ориентированном стиле:
И для этого фильтра есть юнит тест:
Тест очень простой — добавляем двух пользователей и ожидаем, что фильтр вернет только одного из них, возраст которого 20 лет.
При стопроцентном покрытии кода мы имеем только 67% MSI — это уже подозрительно.
Mutation Score Indicator (MSI)
MSI равен 47%. Это ознaчает, что 47% всех сгенерированных мутаций не выжили (убиты, таймауты, ошибки). MSI является главной метрикой мутационного тестирования. Если Code Coverage равен 65%, то мы получаем разницу в 18% и это говорит о том, что процент покрытия строк кода в данном случае является плохим критерием оценки тестов.
Mutation Code Coverage
Данный показатель равен 67%. В целом, он должен быть примерно равен показателю Code Coverage.
Covered Code Mutation Score Indicator
MSI для кода, который покрыт тестами, равен 70%. Данный критерий показывает, насколько эффективны в реальности ваши тесты. То есть это процент всех убитых мутантов, сгенерированных для покрытого тестами кода.
Если проанализировать метрики, получается, что MSI на 18 единиц меньше, чем показатель Code Coverage. Это говорит о том, что тесты гораздо менее эффективны согласно результатам мутационного тестирования, чем по результатам голого Code Coverage.
Давайте посмотрим на сгенерированные мутации.
Запущенные для нее тесты выполняются успешно. То есть изменение исходного кода абсолютно никак не отразилось на результатах теста. Это не то, что нам надо.
Из этой мутации видно, что, тестируя код с условиями на интервалы, всегда надо проверять граничные значения.
Давайте исправим ситуацию и убьём мутанта:
Мы добавили один тест на граничное значение — 18. Теперь, если опять запустить тесты с мутированным кодом, они упадут, так как все значения отфильтруются и вернется пустая коллекция, что, естественно, неверно.
Теперь сигнатура метода абсолютна понятна — передаем в фильтр массив, ожидаем массив.
Запустим еще раз и проверим результат:
Количество мутаций ожидаемо уменьшилось из-за добавления типа возвращаемого значения, и все мутанты убиты. Теперь мы имеем не только Code Coverage 100%, но и Mutation Code Coverage 100%, что является гораздо более показательным критерием качества ваших тестов.
Этот простой пример показывает, что даже при наличии стопроцентного покрытия кода тестами, мутационное тестирование все еще может выявить проблемы и как бы покрывает ваш код «на более чем 100%».
Это позволяет проверить необходимость открытости методов. Если такие мутанты оказываются выжившими, то можно сделать вывод, что публичный интерфейс вашего класса может быть уменьшен и, скорее всего, является избыточным. А в случае с ProtectedVisibility оператором — выживший мутант говорит о том, что метод должен быть изменен на private и нет ни одного наследника у класса, который бы использовал/переопределял родительский protected метод.
Кроме этих двух случаев с выжившим и убитым мутантом, есть и другие. Например, изменение в цикле унарного оператора «++» у переменной счетчика на «—» может привести к тому, что цикл никогда не закончится, т.к. будет бесконечным. Задача фреймворка для мутационного тестирования — корректно обрабатывать такие ситуцаии и помечать мутанта особым статусом — Timeout. Такой исход является положительным и мутант не считается выжившим.
В целом, с теорией разобрались, теперь посмотрим, что представляет собой Infection более детально, и какие есть еще альтернативы для PHP.
Infection PHP
Для работы, Infection требует установленное расширение xDebug для Code Coverage и PHP 7.0+.
Рекомендуемым способом установки, с возможностью автоматического обновления ( infection.phar self-update ), является Phar архив.
На данный момент поддерживаются из коробки два фреймворка для тестирования — PHPUnit (5, 6+) и PhpSpec.
Опции
Из самых интересных опций, с которыми запускается Infection, можно выделить следующие:
—threads
Это количество потоков, работающих параллельно для запуска всего набора сгенерированных мутантов. Значительно ускоряет время выполнения. Но тут есть оговорка: если ваши тесты каким-то образом зависят друг от друга или используют базу данных, использование этой опции может привести к многочисленным упавшим тестам, что крайне негативно скажется на результирующих метриках. Поэтому просматривать лог хотябы на начальных стадиях внедрения все-таки стоит.
—show-mutations
Сразу выводит diff с неубитыми мутантами на консоль, что позволяет моментально анализировать результат и исправлять тест при его написании.
—mutators
Перечисление мутационных операторов, мутирующих код. Удобно, если вы например хотите проверить только PublicVisibility и ProtectedVisibility операторы.
Эти две опции полезны, если вы запускаете Infection как один из шагов процесса сборки вашего проекта на Continious Integration сервере.
—min-msi позволяет указать минимальное значение (в процентах) Mutation Score Indicator. Если указанное значение будет меньше фактического, то билд упадет. Данная опция заставляет при каждом билде покрывать большее количество строк кода.
—min-covered-msi соответственно позволяет указать минимальное значение Covered Code MSI. Данная опция при каждом билде заставляет писать более эффективные и надежные тесты.
Обе опции могут использоваться как по отдельности, так и вместе.
Использование с Travis CI
Каждый релиз (Phar архив) подписывается приватным openssl ключом, поэтому кроме самого архива вам необходимо скачивать и публичный ключ.
Как использовать мутационное тестирование?
Чем может быть полезно мутационное тестирование вам, как разработчику в ваших рабочих или персональных проектах? Как внедрить его в уже имеющийся проект?
Ежедневное использование для разработчика
Мутационное тестирование может быть полезно в ежедневной работе при написании новых тестов. Схема работы выглядит примерно так:
Анализируете выжившие мутанты и пытаетесь добиться хорошего показателя Covered Code MSI — т.е. чтобы процент убитых мутантов из всех сгенерированных для покрытого тестами кода стремился к 100. Это позволит максимально эффективно писать тесты.
При использовании МТ вы заметите, что пишите более лаконичный код с большим количеством тестов. При этом будет использовано покрытие путей (branch coverage), когда все пути вашего кода протестированы, вместо обычного покрытия строк кода (line coverage).
Ежедневное использование в проекте
Мутационное тестирование может быть использовано на Continious Integration сервере. В зависимости от величины проекта, запускать его можно либо при каждом билде, либо реже, как вариант раз в сутки ночью. Тут главное анализировать полученный результат и постоянно улучшать качество тестов.
Например, мутационный фреймворк Infection мутационно тестирует сам себя при каждом билде. И если показатели падают, билд тоже падает.
Почему иногда невозможно добиться 100% MSI?
В мутационном тестировании есть понятие идентичных мутантов. То есть это мутации, которые приводят к идентичному коду с точки зрения логики. Примером такой мутации может служить следующий код:
Смысл в том, что умножение числа и деление числа на ±1 приводит к идентичному результату, и такой мутант оказывается выжившим.
В связи с этим, ожидать для всего кода стопроцентного MSI на практике не стоит. Для этого нужна мощная система регистрации идентичных мутантов и возможность исключения их из рузультирующих метрик.
Альтернативы для PHP
Единственной полноценной работающей альтернативой для Infection в PHP является Humbug — это вообще первый МФ в PHP. Из плюсов, в нем есть экспериментальная поддержка кеширования мутаций (incremental cache). То есть если какой-то файл не меняется и при этом никакие тесты, покрывающие его строки не удалялись при очередном запуске, то мутация не запускается и берется результат последнего запуска. Теоретически, это может значительно увеличить скорость работы, но может привести к ложным срабатываниям и погрешностям в метриках.
С другой стороны, Humbug пока что не поддерживает PHPUnit 6+ и PhpSpec. Однако, главным отличием между Infection и Humbug на текущий момент является то, что Infection использует абстрактное синтаксическое дерево для мутирования кода (Abstract Syntax Tree (AST)). Построение AST возможно благодaря замечательному проекту Никиты Попова — PHP-Parser.
Что же дает использование AST? Рассмотрим подробнее.
Чтобы начать мутировать код, необходимо
Но на самом деле процесс гораздо сложнее, т.к. принятие решения об изменении токена зависит от нескольких условий.
Вот как выглядят реализации мутационного оператора, меняющего «+» на «-» с проверкой массивов:
Очевидно, что использование AST дает огромные преимущества. С ним легче работать, проще поддерживать и разбираться в коде, проще создавать новые мутационные операторы и проще анализировать код, ходя по веткам дерева.
В общем, мутационное тестирование — это еще одно средство для повышения качества ваших тестов и кода в целом, стоящее обращения внимания.
Если у вас есть опыт использования МТ на реальных проектах, или вы попробуете Infection и найдете интересные ошибки в коде — делитесь в комментариях о любых полезных кейсах.
Новый персонаж «Люди Икс». Мнение героев + история.
Предупреждение! В этой версии Курт, Питер и Скотт попадают в эту школу в одно время, как ученики.
Это мой первый тест, не судите строго :з.
Dash Alice Turner
Еще тесты:
Твой парень из Акацуки⚡
Какой жанр фильмов/сериалов, книг тебе подходит?
Кто твой парень из Гарри Поттера?
Твоё рождество в Хогвартсе
Комментариев: 13
Айрис Фрай
Способность: управление светом.
Любимое занятие: рисование.
Характер: вспыльчивая, весёлая, умная, хитрая.
Любимая фраза: Девочки, вы только сами не запутайтесь в этих сплетнях.
До начала учёбы в этой школе жила с матерью и старшей сестрой. О твоих способностях узнала сама и никому долгое время не говорила. Использовала их для того чтобы освещать предметы, которые рисуешь. Потом об этом узнала твоя мама и была очень горда тобой. Твоя сестра после этого начала тебя ещё больше призирать, ты пряталась от неё, становясь невидимой. После этого тебя отдали в эту школу.
Отношение персонажей:
Чарльз Ксавьер: считает способной ученицей, интересуется, что нового ты узнала.
Рейвен Даркхолм: думает, что ты хорошо владеешь своими силами, разговаривает, когда вы пересекаетесь в коридоре.
Эрик Лэншерр: не встречались.
Хенк Маккой: помогает изучить новый материал, считает очень умной, но несконцентрированной.
Джин Грей: ревнует к Скотту, не то чтобы враги, но соперницы
Скотт Саммерс: влюбленны друг в друга, но не знаете об этом
Мойра Мактаггерт: видела тебя, отностится положительно
Курт Вагнер: не общаетесь.
Питер Максимофф: друзья, часто дурачитесь на переменах.
Ороро Монро: лучшие подруги, вместе занимаетесь, часто помогаешь ей с некоторыми уроками, а она пытается свести тебя со Скоттом.
Алекс Саммерс: считает, что ты и Скотт милая пара, шипперит вас.
Логан: виделись несколько раз.
Апокалипсис: считает, что ваша с Шторм команда очень сильна, опосается вас.
История:
Монро сидела напротив тебя и пыталась заглянуть в скетчбук, но ты ловко уворачивалась.
— Да, ладно, что ты там рисуешь? — поинтересовалась подруга.
— Ничего, — ответила ты, так как не очень любила показывать свои работы до их завершения.
— Ну-у-у, тогда всё ясно, — с улыбкой протянула Шторм.
— Что понятно? — решила переспросить ты, мало ли что придёт ей в голову.
— Ты рисуешь Скотта! — воскликнула она, — Потому что он тебе нравится!
— Нет, — ты смутилась и опустила глаза в блокнот на котором была изображена фигура человека. Пока ещё не какого-то конкретного человека, — не говори глупостей. Только такие как ты будут нас сводить, — покрутила у виска пальцем.
— Да ну? — ухмыльнулась подруга, — Эй, Алекс, — позвала она старшего Саммерса, проходящего мимо, — как ты считаешь, кто подходит твоему брату?
До завершения предложения парень стоял с растерянным видом, но когда Шторм закончила, расплылся в улыбке и сказал:
— Я всё же считаю, что ему подходит Айрис, потому что-
Договорить он не успел, ведь в него полетели канцелярские пренадлежности, и ему пришлось уворачиваться.
— Быстрее, — Алекс, занимающий почётную должность помощника/учителя/организатора в этой школе, вёл тебя по коридору.
— А в чём заключается моя помощь? — решила уточнить ты.
— Сейчас узнаешь.
В один из кабинетов на верхнем этаже была открыта дверь, через щель которой на пол мягко падал свет. Тебе захотелось взять в руки карандаши тотчас же это зарисовать, но школьные дела были важней.
Алекс пропустил тебя вперёд, и только ты сказала «Что-«, за тобой с грохотом захлопнулась дверь и щёлкнул замок.
— Алекс, какого, — раздался голос Скотта. Парень всё это время был здесь, просто стоял в углу.
— Мы вас не выпустим, пока не поговорите, — послышался голос Монро.
— Да нам не о чём разговаривать, он же идиот!
— Вот, спасибо, — произнёс Скотт, — ты вообще-то тоже не подарочек!
— Ну, извини, и праздник не каждый день, — ты развела руками.
Но те, кто был по ту сторону двери, открывать её не собирались.
Несколько минут ругательств и вот вы сидите на полу, облокотившись о ту самую дверь.
— Эх, — шумно выдохнул Скотт, — ладно, я начну. Фрай, ты замечательная, мне нравятся твои волосыи то, как ты рисуешь.
— Ещё недавно же говорил, что мне не стоит этим заниматься, — ты расплылась в улыбке.
— Ну, я соврал.
— А ты всегда врёшь девушкам, чьи волосы тебе нравятся?
— Чёрт, в моей голове это звучало эффектней.
Вы рассмеялися.
— На самом деле, — успокоившись произнесла ты, — ты тоже очень классный.
Неловкая пауза.
— Айрис?
-М-м-м? — задумчего протянула ты, отметив, что до этого он называл тебя только по фамилии.
— Может быть ты согласишься встречаться со мной?
Ты стояла в ступоре и всё, что смогла из себя выдавить:
— Да, почему бы и нет?
— Отлично, — он обнял тебя.
За дверью раздались крики и аплодесменты, надо же, а вы оба уже забыли о том, что вас здесь заперли. В кабинет зашли Шторм и Алекс, но ребята продолжали обниматься, а на тумбочке в твоей комнате лежал тот самый рисунок человека, у которого были явно изображены черты Скотта Саммерса. ♡♡♡
Беатрис Хардинг)
Боооже, тест супер.
❤️❤️❤️❤️❤️❤️❤️❤️
Способность: управление светом.
Любимое занятие: рисование.
Характер: вспыльчивая, весёлая, умная, хитрая.
Любимая фраза: Девочки, вы только сами не запутайтесь в этих сплетнях.
До начала учёбы в этой школе жила с матерью и старшей сестрой. О твоих способностях узнала сама и никому долгое время не говорила. Использовала их для того чтобы освещать предметы, которые рисуешь. Потом об этом узнала твоя мама и была очень горда тобой. Твоя сестра после этого начала тебя ещё больше призирать, ты пряталась от неё, становясь невидимой. После этого тебя отдали в эту школу.
Отношение персонажей:
Чарльз Ксавьер: считает способной ученицей, интересуется, что нового ты узнала.
Рейвен Даркхолм: думает, что ты хорошо владеешь своими силами, разговаривает, когда вы пересекаетесь в коридоре.
Эрик Лэншерр: не встречались.
Хенк Маккой: помогает изучить новый материал, считает очень умной, но несконцентрированной.
Джин Грей: ревнует к Скотту, не то чтобы враги, но соперницы
Скотт Саммерс: влюбленны друг в друга, но не знаете об этом
Мойра Мактаггерт: видела тебя, отностится положительно
Курт Вагнер: не общаетесь.
Питер Максимофф: друзья, часто дурачитесь на переменах.
Ороро Монро: лучшие подруги, вместе занимаетесь, часто помогаешь ей с некоторыми уроками, а она пытается свести тебя со Скоттом.
Алекс Саммерс: считает, что ты и Скотт милая пара, шипперит вас.
Логан: виделись несколько раз.
Апокалипсис: считает, что ваша с Шторм команда очень сильна, опосается вас.
История:
Монро сидела напротив тебя и пыталась заглянуть в скетчбук, но ты ловко уворачивалась.
— Да, ладно, что ты там рисуешь? — поинтересовалась подруга.
— Ничего, — ответила ты, так как не очень любила показывать свои работы до их завершения.
— Ну-у-у, тогда всё ясно, — с улыбкой протянула Шторм.
— Что понятно? — решила переспросить ты, мало ли что придёт ей в голову.
— Ты рисуешь Скотта! — воскликнула она, — Потому что он тебе нравится!
— Нет, — ты смутилась и опустила глаза в блокнот на котором была изображена фигура человека. Пока ещё не какого-то конкретного человека, — не говори глупостей. Только такие как ты будут нас сводить, — покрутила у виска пальцем.
— Да ну? — ухмыльнулась подруга, — Эй, Алекс, — позвала она старшего Саммерса, проходящего мимо, — как ты считаешь, кто подходит твоему брату?
До завершения предложения парень стоял с растерянным видом, но когда Шторм закончила, расплылся в улыбке и сказал:
— Я всё же считаю, что ему подходит Айрис, потому что-
Договорить он не успел, ведь в него полетели канцелярские пренадлежности, и ему пришлось уворачиваться.
— Быстрее, — Алекс, занимающий почётную должность помощника/учителя/организатора в этой школе, вёл тебя по коридору.
— А в чём заключается моя помощь? — решила уточнить ты.
— Сейчас узнаешь.
В один из кабинетов на верхнем этаже была открыта дверь, через щель которой на пол мягко падал свет. Тебе захотелось взять в руки карандаши тотчас же это зарисовать, но школьные дела были важней.
Алекс пропустил тебя вперёд, и только ты сказала «Что-«, за тобой с грохотом захлопнулась дверь и щёлкнул замок.
— Алекс, какого, — раздался голос Скотта. Парень всё это время был здесь, просто стоял в углу.
— Мы вас не выпустим, пока не поговорите, — послышался голос Монро.
— Да нам не о чём разговаривать, он же идиот!
— Вот, спасибо, — произнёс Скотт, — ты вообще-то тоже не подарочек!
— Ну, извини, и праздник не каждый день, — ты развела руками.
Но те, кто был по ту сторону двери, открывать её не собирались.
Несколько минут ругательств и вот вы сидите на полу, облокотившись о ту самую дверь.
— Эх, — шумно выдохнул Скотт, — ладно, я начну. Фрай, ты замечательная, мне нравятся твои волосыи то, как ты рисуешь.
— Ещё недавно же говорил, что мне не стоит этим заниматься, — ты расплылась в улыбке.
— Ну, я соврал.
— А ты всегда врёшь девушкам, чьи волосы тебе нравятся?
— Чёрт, в моей голове это звучало эффектней.
Вы рассмеялися.
— На самом деле, — успокоившись произнесла ты, — ты тоже очень классный.
Неловкая пауза.
— Айрис?
-М-м-м? — задумчего протянула ты, отметив, что до этого он называл тебя только по фамилии.
— Может быть ты согласишься встречаться со мной?
Ты стояла в ступоре и всё, что смогла из себя выдавить:
— Да, почему бы и нет?
— Отлично, — он обнял тебя.
За дверью раздались крики и аплодесменты, надо же, а вы оба уже забыли о том, что вас здесь заперли. В кабинет зашли Шторм и Алекс, но ребята продолжали обниматься, а на тумбочке в твоей комнате лежал тот самый рисунок человека, у которого были явно изображены черты Скотта Саммерса. ♡♡♡
Мутанты, убийства — это о чем? О тестировании
“Попробуем разобраться, что такое мутационное тестирование, и как эта концепция работает в Pitest. В конце посмотрим, как в реальном проекте ищутся баги таким специфическим тестированием.
Введение
Доказано на сотнях примеров, что недостаточно протестированный софт приводит к высоким затратам, отложенному выходу продукта, к недовольству клиентов, и разумеется плохому имиджу компании в которой создан продукт. Бывают и совсем вопиющие случаи, например авиакатастрофы.
Пирамида тестирования, знакомо? Да, всегда приходится чем-то жертвовать
Тестировщик вносит свой вклад в качество тем, что создает тесты, и в 2021 году это в основном, все-таки юнит-тесты.
Как известно, самый простой и быстрый способ оценить общее качество тестирования в проекте — измерить покрытие. Бывают следующие основные типы покрытия:
Когда уже есть метрика (то есть этот показатель покрытия), можно ставить цели. Например, в нашей компании (Sipios, финтех) мы говорим, что как минимум 80% ветвлений должно быть покрыто, а иначе ты не можешь мержить свой pull request в мастер-ветку. Нужно быть внимательным при достижении этого лимита — малое покрытие означает недостаточное тестирование, а высокое покрытие вовсе не гарантирует хорошего качества тестов.
Самый простой пример визуализировать эту ситуацию — ты можешь покрыть всю свою кодовую базу при тестировании, и не проверить толком ничего, как оказывается иногда постфактум. В случае покрытия ветвлений, это из-за того что несложные ветвления очень легко покрыть.
В такой ситуации, для улучшения надежности, иногда применяется такой специфический тип тестирования, как мутационное тестирование.
Что это такое?
Сама идея отнюдь не нова, впервые предложена одним из гуру 1970х, Ричардом Липтоном, почти 50 лет назад. Как говорит Википедия, она базируется на двух столпах:
Во первых, программист по умолчанию считается очень опытным. Отсюда считается, что в коде грубейших логических ошибок нет!, а 99% ошибок — синтаксические, то есть легко находимые и корректируемые.
Во вторых, «эффект сцепления»: иногда простая ошибка «тянет за собой каскад других ошибок, сцепленных с ошибкой-родоначальником».
Процесс мутационного тестирования: 1. Создаются «мутанты». 2. Мутантов пытаются убить. И смотрят, что из этого получается.
Создание мутантов
Итак, процесс начинается с генерирования чуть отличающихся версий кода, что и называется «мутацией».
Если ты знаешь, что такое Generic Algorithm (GA) применяемый для оптимизации кода и поиска проблемных мест, он может рассматриваться как первый этап в создании «популяции мутантов».
Этот метод требует только твоего кода и выбора мутационных операторов. Далее применяем эти операторы, один раз для каждого выражения в программе. Результат применения мутационного оператора и называется «мутантом».
Мутационные операторы бывают такими:
В нашем случае мы достигли 100% покрытия по строчкам, при 50%-ном мутационном покрытии (скриншот):
Можно уточнить покрытие по каждому классу, кликая по ним. Pitest показывает, какие мутанты выжили, и какие мутационные операторы к ним применялись:
Как и говорилось в предыдущей части, можно предметно пробовать улучшить качество тестов, путем добавления теста который отлавливает первого мутанта (поскольку он как бы тянет за собой каскадом много других мутантов).
После добавления этого нового теста можем настроить минимальный порог мутационного покрытия: добавляем опцию — DmutationThreshold
Другие инструменты
Если работаешь не с Java, советую посмотреть список опенсорсных проектов для мутационного тестирования, там есть много чего, от JS до Rust.
Личные впечатления
Расскажу о своем достаточно длительном опыте с мутационным тестированием. Нет 100% гарантии, что создание мутантов и их отлов всегда и везде улучшает качество любых тестов. Об этом дальше.
Основные ошибки
Слишком много мутантов
Генерация мутантов в каждом выражении, с множеством мутированных операторов — призовет армию мутантов, с которой ты не справишься. Придется запускать тесты по каждому из них. Это процесс, требующий долгих вычислений, и если нет внимательности, вдумчивости при их создании, на анализ придется потратить очень много времени.
Я работаю в проекте с 15-тью микросервисами, и я добавил конфигурацию с Pitest в главный pom.xml. Начал без фиксации на определенном классе или пакете, поскольку мои юнит-тесты могут размещаться в разных подпроектах. Я получил около 5 тысяч мутантов на этих 15 микросервисах — это довольно большое число.
Избегай бесполезных мутантов
Некоторые мутанты не интересны, особенно те, что генерируются из DTO. Pitest может генерировать мутации на методах, предоставленных по аннотации, типа @Data из Lombok. Это мутанты, которых нужно избегать, потому что придется оверрайдить множество методов из аннотаций.
Мутация интеграционных тестов
Интеграционные тесты требуют сильно больше времени чем юнит-тесты. По умолчанию Pitest работает с таймаутом 4 секунды (чтобы не заходить в бесконечный цикл). Если интеграционные тесты медленные, возможно, надо будет ждать по 4 секунды по каждому тесту сгенерированных мутантов. Другими словами, если все это делать “правильно”, то потребуется несколько дней, если не больше.
Репорт уже нечитаемый
Можешь сгенерировать отчет по всему коду и по всем тестам. Это заберет много времени, и это то что иногда бесит в сфере автоматизации (например у меня заняло 23 секунды протестировать микросервис с лишь 15-тью юнит-тестами) и у тебя будет слишком много выживших мутантов.
Представь что у тебя 90% мутационный показатель на 5000 мутантах. Даже при таком показателе получается, что надо проанализировать около 500 мутантов. Думаю, лучше подходить к делу проще, подбирать количество мутантов так, чтобы потом отчет был читаемым. Процесс выполнения мутационного тестирования и анализа отчета всегда занимает ощутимое время.
Мутируй-убивай по умному
Сначала надо найти критические места в проекте. В моем случае, это был небольшой API-сервис, который делала моя команда.
Я выбрал этот сервис, поскольку это была лог API. Выбранный сервис был подходящим с точки зрения кода, поскольку в нем было лишь около 1000 строчек, и в него вносили правки 18 разработчиков (то есть код должен был быть прямо-таки переполнен случайными ошибками). Круче всего было, для мутационного тестирования, что определенные части кода были написаны больше года назад, а некоторые — лишь на прошлой неделе.
И да, этот сервис был “официально” покрыт, с точки зрения количества строчек, на 96%, и с точки зрения ветвлений на 93%.
Это был типичнейший API-сервис, куда разработчики вносили свои изменения довольно быстро, и QA-команда проверяла их. Итак, мы попытались внести изменения, и посмотрели, «поломается» ли код.
Разбираем свои результаты
Анализ показателей
Первое что я заметил после генерации отчета — лишь 50% покрытия строчек юнит-тестами, и 34% мутационный показатель.
Я удивился такому низкому покрытию, потому что код выглядел плохо оттестированным, но в принципе это же понятно. Мы постоянно добавляли в код новые методы. Дальше мы попытались провести интеграционные тесты. Интеграционные тесты как правило активно задействуют весь сервис, со всеми методами, итого получается высокое покрытие. Когда достигается уровень покрытия 80% (неформальный стандарт в нашей индустрии) — ты не спешишь писать юнит-тесты, потому что видишь хорошую метрику 80%, а она говорит, что в общем-то, к покрытию формальных претензий нет.
Не знаю, как истолковать мутационный показатель лишь 34%. Выглядит не так уж плохо с формальной точки зрения. Также выяснилось, что интеграционные тесты тоже убивают некоторую часть мутантов. Но в целом оказалось, что 66% мутантов смогли пережить все тесты!
Смотрим, кто выжил
Я сосредоточился на нескольких мутационных операторах. В моем сервисе это были операторы, которые выживали чаще всех:
Предполагаю, что эти мутации выражений — самые простые, с которых и надо начинать мутационное тестирование. Да, иногда надо лишь удалить строчку, и посмотреть, будет ли работать такой код. В большинстве случаев на практике, такие мутанты невольно случаются в методах-сеттерах в объектах. Иногда же они случаются в API-вызовах — но вполне вылавливаются на этапе интеграционных тестов.
Нулевые возвращаемые значения (Null return values) — также несложные для анализа. Они, кстати, могут создавать грандиозные проблемы разработчику. Такие значения могут возникать при удалении сеттеров. Например создание целого каскада “мутантов высокого порядка” случайным удалением сеттера. Считаю, было бы любопытно проверить это, в рамках так называемого “безопасного программирования”.
Итак, что я вынес из этой истории
Мутационное тестирование работает
Проверив результаты тестирования лишь нескольких мутационных операторов, я смог завести свой первый баг с применением мутационного тестирования. Это заняло у меня полчаса времени и лишь одно инвертирование условия.
Это могло бы выглядеть плохо, потому что когда рефакторят код, легко упустить подобные ошибки, “мутационного типа”. На практике, почти всегда идет длительный процесс, в котором вылавливаются почти все такие опечатки, до того как баг попадет в деплой. Да, надо стараться тестировать методы локально, до того как сделать запрос на слияние кода. И здесь мне удалось выловить баг. Потом, у нас есть ревью кода, на котором коллеги должны были выловить этот баг. Потом, функция тестируется Владельцем Продукта уже в девелопмент-окружении, и потом тестировщиками в пре-продакшене.
Вместе с тем, следуя нашей правильной Lean-методике, мы знаем, что чем раньше выловим ошибки, тем лучше. Мы не тратим время попусту. Поэтому я думаю, что мутационное тестирование — потенциально крутая вещь, заставляющая разработчиков понимать полезность тестирования, способна гарантировать, что функция будет все равно работать как положено, даже если какой-то разработчик случайно внесет ошибки.
Если это в первый раз
Именно мутационное тестирование помогло нам понять, что наш рабочий процесс реально правильный. У нас он достаточно длительный, не пропускающий баги до этапа продакшена. Думаю, мутационное тестирование должно проводиться после заведения багов, чтобы улучшить рабочий процесс, особенно если работаешь в отделе тестирования безопасности.
Я также понял, что мутационным тестированием мы можем стимулировать аккуратность наших разработчиков. Разница между интеграционными тестами и юнит-тестами должна хорошо соблюдаться, и должны применяться оба типа тестов. Предполагаю, что TDD-процесс (“разработки через тестирование”) будет полезен в этом, если сосредоточиться сначала на юнит-тестах, а потом на интеграционных Это повысит мутационный показатель.
Мутационное тестирование я буду применять в своем будущем рефакторинге. К сожалению, еще не хватает хороших инструментов для анализа результатов.”