Skip to content

date: 2026-06-08 tags: [mutation, infection, pest, dead-end, ci] status: active graduated_to:

Infection dropped its Pest adapter — a full migration off pest --mutate is a dead end

Symptom — evaluating a CI-cost handover that assumed Infection, a spike to migrate mutation testing from Pest's --mutate to Infection failed on this stack (Pest 4 / PHPUnit 12 / PHP 8.5), even after correcting an initial misconfiguration.

Root cause — Infection has no Pest adapter. It added a dedicated PestAdapter in 0.23.0 (2021, PR #1516, closing issue #1476) but has since removed it: no PestAdapter class exists in the latest stable (0.33.2, May 2024) or dev-master, and the config schema's testFramework enum is phpunit / phpspec / codeception / testo only. The remaining phpunit-adapter fallback breaks on Pest 4 / PHPUnit 12 two ways:

  1. Infection's InitialConfigBuilder injects a legacy PHPUnit-9 <filter><whitelist> element; PHPUnit 12 validates strictly and Pest exits 1 even with every test green → Infection's initial-test gate aborts. (PR #1294 only suppresses this when a <coverage> block exists — our phpunit.xml uses the newer <source> element, so the guard never fires.)
  2. Pest records executing test classes as P\Tests\… in coverage but Tests\… in junit, so Infection can't map coverage→tests (Could not find any information for the test).

The PHPUnit-12 fixes that do exist (#2440 → PR #3043, Apr 2026) are unreleased and target the phpunit adapter, not Pest. Forcing it via --skip-initial-tests + external coverage hits break #2 and risks false-green mutants (escaped reported as killed).

Fix — none attempted; the migration was rejected. Stay on Pest's native pest --mutate. The real speed win (multi-core parallelism) comes from a self-hosted runner, not a different engine.

Guard — this signpost + CLAUDE.md's Mutation Testing Policy, which mandates pest --mutate. Re-open only if pestphp/pest#4 or infection#1521 ships a maintained Pest adapter.