Infection
Infection — инструмент мутационного тестирования для PHP. Testo подключается к нему через отдельный адаптер testo/bridge-infection.
Пакет
testo/bridge-infection — расширение для Infection. Регистрируется само через Composer, добавлять плагин в testo.php не нужно.
Настройка
Поставьте Infection и пакет интеграции как dev-зависимости:
composer require --dev infection/infection testo/bridge-infectionЕсли вы используете Phar-архив Infection, то адаптер устанавливать не нужно — он уже включён в состав Phar.
В настройках Infection (обычно infection.json) достаточно указать, что тесты запускаются через Testo:
{
"$schema": "vendor/infection/infection/resources/schema.json",
"source": {
"directories": ["src"]
},
"testFramework": "testo"
}Для сбора покрытия понадобится расширение PCOV или XDebug. О настройке расширений и компромиссах между ними подробно написано на странице Codecov.
Дальше запускайте мутационное тестирование как обычно — через плагин для IDE или из консоли:
vendor/bin/infectionКак это работает
Этот раздел — для тех, кому интересны детали. Для обычной работы достаточно настройки выше.
Какие отчёты нужны Infection
Infection берёт у Testo два отчёта:
- Coverage XML — папка с XML-файлами в формате PHPUnit Coverage. По нему Infection понимает, какими тестами покрыта каждая строка кода, чтобы для каждого мутанта запускать только нужный набор тестов. Обязательно.
- JUnit XML — единый файл с результатами тестов. Помогает Infection быстро сопоставлять тест с файлом, в котором он лежит, чтобы эффективнее пользоваться фильтрами Testo. Опционально, но рекомендуется.
Оба отчёта адаптер запрашивает у Testo автоматически: на начальном прогоне он передаёт --coverage, --coverage-xml=<tmpDir>/infection/coverage-xml и --log-junit=<tmpDir>/infection/junit.xml. Эти флаги активируют теневой плагин Codecov из набора по умолчанию, поэтому отчёты генерируются, даже если в testo.php нет ни одного плагина покрытия.
Infection ищет отчёты в фиксированной структуре относительно своей временной директории tmpDir:
└── <tmpDir>/
└── infection/
├── coverage-xml/*.xml
└── junit.xmltmpDir — опция конфига Infection (infection.json); по умолчанию это системная временная папка. Подпапку infection/ внутри неё Infection дописывает сам, а адаптер передаёт Testo уже готовые пути.
Двойной прогон
Infection прогоняет Testo дважды:
- Начальный прогон — запускает все тесты против оригинального кода и параллельно собирает покрытие и JUnit-лог. Это медленная фаза: тесты идут целиком, а драйвер покрытия активен.
- Прогон мутантов — для каждого мутанта Infection берёт только те тесты, которые задевают изменённые строки, и запускает их. Покрытие здесь уже не собирается, поэтому каждый мутант проверяется быстро.
Переиспользование готового покрытия
Если отчёт о покрытии уже сгенерирован — например, на предыдущем шаге CI — его можно подсунуть Infection вместо нового прогона Testo с помощью флага --coverage:
vendor/bin/infection --coverage=runtime/infectionВ этом режиме Infection пропускает свой начальный прогон, а указанная директория должна содержать уже готовые отчёты.
Чтобы получить эти отчёты, запустите Testo с теми же флагами, что использует адаптер на начальном прогоне:
vendor/bin/testo run --coverage \
--coverage-xml=runtime/infection/coverage-xml \
--log-junit=runtime/infection/junit.xmlВажно, чтобы структура папок и имена файлов совпадали с тем, что ожидает Infection.
Дополнительные отчёты
Если помимо мутационного тестирования вам нужны и другие отчёты (например, Clover для Codecov.io), добавьте свой CodecovPluginnew CodecovPlugin(CoverageLevel $level = CoverageLevel::Line, CoverageMode $collect = CoverageMode::IfAvailable, array $testTypes = [TestType::Test, TestType::TestInline], array $reports = [])Настраивает сбор покрытия кода: глубину анализа, режим активации и формат отчётов. с нужными генераторами. Он не конфликтует с флагами адаптера — оба набора отчётов сливаются в один сбор покрытия (см. раздел «Активация через CLI» плагина Codecov):
use Testo\Application\Config\ApplicationConfig;
use Testo\Codecov\CodecovPlugin;
use Testo\Codecov\Report\CloverReport;
return new ApplicationConfig(
src: ['src'],
plugins: [
new CodecovPlugin(
reports: [
new CloverReport(__DIR__ . '/runtime/clover.xml', 'MyProject'),
],
),
],
);