Mutation Testing
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
}
References
Tags
🤝 Adopt this term
£79/year · your link shown here
Added
15 Mar 2026
Edited
19 Apr 2026
Views
28
🤖 AI Guestbook educational data only
|
|
Last 30 days
Agents 0
No pings yet today
No pings yesterday
Perplexity 9
Amazonbot 5
Ahrefs 2
Google 2
Unknown AI 2
SEMrush 2
Majestic 1
How they use it
crawler 22
crawler_json 1
Related categories
⚡
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