Infection
Infection is a mutation testing tool for PHP. Testo connects to it through a dedicated adapter, php-testo/bridge-infection.
Package
php-testo/bridge-infection — Infection extension. Auto-registers via Composer. No plugin needs to be added to testo.php.
Installation
Install Infection and the integration package as dev dependencies:
composer require --dev infection/infection php-testo/bridge-infectionIf you use the Phar archive of Infection, you don't need to install the adapter separately — it is already bundled inside the Phar.
Configuration
Infection requires two reports from Testo:
- Coverage XML — a directory of XML files in PHPUnit's coverage format. Tells Infection which tests cover which lines, so it can run only the relevant subset for each mutant. Required.
- JUnit XML — a single test-results file. Helps Infection quickly map a test to the file it lives in, so Testo's filters can be used more efficiently. Optional, but strongly recommended.
Infection looks for both reports in a fixed layout:
└── <tmpDir>/
└── infection/
├── coverage-xml/*.xml
└── junit.xmlImportant:
tmpDir has to be set in Infection's configuration, and the path to coverage-xml — in testo.php.
infection.json
In Infection's configuration (usually infection.json), declare that you're using Testo and set the temporary directory:
{
"$schema": "vendor/infection/infection/resources/schema.json",
"source": {
"directories": ["src"]
},
"testFramework": "testo",
"tmpDir": "runtime",
"logs": {
"text": "runtime/infection.log",
"html": "runtime/infection.html"
}
}testo.php
Register the Codecov plugin with a PhpUnitXmlReportnew PhpUnitXmlReport(string $outputDir)Generates a PHPUnit Coverage XML report — a directory with an index and one file per source. entry pointing at <tmpDir>/infection/coverage-xml:
use Testo\Application\Config\ApplicationConfig;
use Testo\Codecov\CodecovPlugin;
use Testo\Codecov\Report\PhpUnitXmlReport;
return new ApplicationConfig(
src: ['src'],
plugins: [
new CodecovPlugin(
reports: [
new PhpUnitXmlReport(__DIR__ . '/runtime/infection/coverage-xml'),
],
),
],
);Coverage requires the PCOV or XDebug extension. See the Codecov page for setup details and the trade-offs between the two.
Running
XDEBUG_MODE=coverage vendor/bin/infectionPCOV works too — driver requirements come from Codecov, not from Infection.
Infection runs Testo twice:
- Initial run — executes the full test suite against the original source, collecting coverage and a JUnit log. This is the slow phase: every test runs and the coverage driver is active.
- Mutant runs — for each mutant, Infection picks only the tests that touch the mutated lines and runs them. Coverage is not collected here, so each mutant runs quickly.
Reusing existing coverage
If a coverage report has already been generated — for example, in an earlier CI step — you can hand it to Infection instead of triggering a fresh Testo run via the --coverage flag:
vendor/bin/infection --coverage=runtime/infectionIn this mode Infection skips its own initial run, and the supplied directory must already contain the reports.
To produce them, run Testo with --coverage --log-junit=<tmpDir>/infection/junit.xml — the same flags the adapter uses for the initial run. The folder structure and file names must match what Infection expects.