← Home ← Codex ← DEBT
Browse by Category
+ added · updated 7d
← Back to glossary

Mutation Testing

Testing PHP 7.1+ Advanced
debt(d5/e3/b3/t7)
d5 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'specialist tool catches it' (d5). The detection_hints.tools list includes Infection PHP, a specialist mutation testing tool — it's not caught by the compiler or a default linter, but a dedicated tool (Infection) can surface surviving mutants when explicitly run. PHPUnit and Pest alone won't flag the problem; Infection must be deliberately integrated into the pipeline.

e3 Effort Remediation debt — work required to fix once spotted

Closest to 'simple parameterised fix' (e3). The quick_fix describes running Infection PHP on the unit test suite and adding assertions that verify actual output. This is typically a small, localised fix per surviving mutant — adding or strengthening assertions in a test file — but it may touch multiple test cases across one component, placing it at e3 rather than a trivial one-liner.

b3 Burden Structural debt — long-term weight of choosing wrong

Closest to 'localised tax' (b3). Mutation testing is a diagnostic/quality tool rather than a load-bearing architectural choice. It applies to specific test suites (web, cli, queue-worker contexts) and its cost is borne mainly by the team maintaining the test suite. Common mistakes note it should be scoped to critical domain logic, limiting its reach. It doesn't shape the whole codebase architecture, keeping burden at b3.

t7 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'serious trap' (t7). The misconception field directly states the trap: developers believe 100% code coverage means tests are thorough, but mutation testing proves otherwise. This contradicts the widely held mental model that coverage metrics are a reliable proxy for test quality — a belief reinforced by tooling defaults (coverage reports) across the industry. This is a well-documented gotcha that contradicts how a similar concept (code coverage) is understood, warranting t7.

About DEBT scoring →

Also Known As

mutation score Infection PHP mutant

TL;DR

A technique that automatically modifies source code and checks whether tests fail — surviving mutations indicate test gaps even where line coverage appears complete.

Explanation

A mutation testing tool makes small changes (mutations) to the source code — flipping a condition, changing an operator, removing a statement — then runs the test suite. If the tests still pass, the mutation 'survived', meaning no test catches that change. Mutation score = killed mutations / total mutations. A 90% line coverage suite with 40% mutation score has poor actual test quality. Infection is the PHP mutation testing framework.

Diagram

flowchart TD
    SRC[Source Code] --> MT[Mutation Tool<br/>Infection PHP]
    MT -->|Creates| M1[Mutant 1<br/>+ changed to -]
    MT -->|Creates| M2[Mutant 2<br/>if removed]
    MT -->|Creates| M3[Mutant 3<br/>true -> false]
    M1 & M2 & M3 --> SUITE[Test Suite]
    SUITE -->|Tests fail| KILLED[Mutant Killed<br/>good test]
    SUITE -->|Tests pass| SURVIVED[Mutant Survived<br/>test gap found]
style KILLED fill:#238636,color:#fff
style SURVIVED fill:#f85149,color:#fff

Common Misconception

100% code coverage means tests are thorough — mutation testing proves otherwise; covered code with no meaningful assertions produces surviving mutants.

Why It Matters

Mutation testing catches tests that assert nothing meaningful — they pass even when production code is wrong, providing false confidence in coverage metrics.

Common Mistakes

  • Running mutation testing on the full codebase at once — start with critical domain logic only; mutation testing is slow.
  • Targeting 100% mutation score — some mutations are equivalent (same behaviour, different code) and should be ignored.
  • Not interpreting surviving mutants carefully — some indicate test gaps, others are in unreachable code paths.
  • Using mutation testing instead of good test design — it is a diagnostic tool, not a substitute for thinking about what to test.

Code Examples

✗ Vulnerable
// Test with coverage but no real assertion:
public function testCalculate(): void {
    $calc = new Calculator();
    $result = $calc->add(2, 3); // Line executed — covered
    $this->assertTrue(true);   // No assertion on result — mutation survives!
    // Mutant: return $a - $b; — test still passes
}
✓ Fixed
// Meaningful assertion — kills the mutant:
public function testCalculate(): void {
    $calc = new Calculator();
    $this->assertSame(5, $calc->add(2, 3));
    $this->assertSame(-1, $calc->add(2, 3, subtract: true));
    // Mutant: return $a - $b; now fails the first assertion
}

Added 15 Mar 2026
Edited 19 Apr 2026
Views 60
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
1 ping T 0 pings W 1 ping T 0 pings F 0 pings S 0 pings S 0 pings M 1 ping T 1 ping W 0 pings T 1 ping F 0 pings S 5 pings S 1 ping M 1 ping T 0 pings W 1 ping T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 0 pings F 0 pings S 1 ping S 0 pings M 1 ping T 0 pings W
No pings yet today
Ahrefs 1
Perplexity 10 Scrapy 8 Amazonbot 6 Ahrefs 5 Google 4 SEMrush 4 Unknown AI 2 Claude 2 Majestic 1 Bing 1 Meta AI 1 PetalBot 1
crawler 42 crawler_json 3
DEV INTEL Tools & Severity
🟡 Medium ⚙ Fix effort: High
⚡ Quick Fix
Run Infection PHP on your unit test suite — a mutant surviving means your test didn't detect a change in behaviour; fix by adding assertions that test the actual output
📦 Applies To
PHP 7.1+ web cli queue-worker
🔗 Prerequisites
🔍 Detection Hints
High line coverage with poor behaviour verification; tests that pass even when logic is deleted; assertions only checking return type not actual value
Auto-detectable: ✓ Yes infection phpunit pest
⚠ Related Problems
🤖 AI Agent
Confidence: Low False Positives: Medium ✗ Manual fix Fix: Medium Context: File Tests: Update


✓ schema.org compliant