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

Test Coverage Types

Testing PHP 7.0+ Intermediate
debt(d5/e3/b5/t9)
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 phpunit, xdebug, pcov, and infection — these are specialist tools that must be explicitly configured and run. The problem (tests that execute but don't assert, branches not covered, no mutation testing) is not caught by a compiler or default linter; it requires running coverage reports and mutation testing tooling like Infection PHP. Slightly aligned to d5 because coverage and mutation tools are available but non-default and require deliberate setup.

e3 Effort Remediation debt — work required to fix once spotted

Closest to 'simple parameterised fix' (e3). The quick_fix states switching from line coverage to branch coverage as the primary metric and adding mutation testing. This is a configuration and process change — updating phpunit/xdebug configuration, adjusting CI thresholds, and adding Infection PHP. It's more than a single-line patch (not e1) but is largely localised to CI configuration and test suite setup rather than touching multiple application files, placing it at e3.

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

Closest to 'persistent productivity tax' (b5). The misconception and common_mistakes indicate that once a team adopts 100% line coverage as the sole metric, it shapes every testing decision going forward — developers write coverage-padding tests instead of meaningful ones. This applies across web, cli, and queue-worker contexts (broad applies_to). It slows down many work streams by creating false confidence and technical debt in the test suite, but it doesn't define the entire system architecture, placing it at b5.

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

Closest to 'catastrophic trap — the obvious way is always wrong' (t9). The misconception field states explicitly: '100% code coverage means the code is fully tested.' This is the canonical, nearly universal wrong belief. The why_it_matters confirms teams targeting 100% line coverage have false confidence providing zero protection against regressions. The obvious, intuitive interpretation of '100% coverage' directly contradicts the actual safety guarantee. This is a textbook t9: the natural, competent reading of the metric is always misleading.

About DEBT scoring →

Also Known As

code coverage mutation testing branch coverage Infection PHP

TL;DR

Line coverage (lines executed), branch coverage (if/else paths), mutation testing (do tests detect actual bugs) — each measures a different aspect of test quality.

Explanation

Line coverage: percentage of source lines executed by tests — easiest to achieve, easiest to game (execute a line without asserting its output). Branch coverage: percentage of if/else/switch branches taken — more meaningful than line coverage. Mutation testing (Infection PHP): automatically modifies code (change + to -, flip conditions) and checks if tests fail — the mutation score reveals whether tests actually detect changes. 100% line coverage with no assertions = 100% line coverage, 0% mutation score.

Common Misconception

100% code coverage means the code is fully tested — 100% line coverage can be achieved with tests that execute every line but assert nothing; mutation testing reveals whether tests actually catch bugs.

Why It Matters

A team targeting 100% line coverage without mutation testing has false confidence — tests that execute but don't assert provide zero protection against regressions or logic errors.

Common Mistakes

  • 100% line coverage as the only quality metric
  • Tests that cover code without asserting outcomes
  • Testing implementation details instead of observable behaviour
  • Not running mutation testing on critical business logic paths

Code Examples

✗ Vulnerable
// 100% line coverage, 0% useful assertions:
public function testCalculateTotal(): void {
    $cart = new Cart();
    $cart->add(new Item('Widget', 9.99));
    $total = $cart->calculateTotal();
    // No assertion — executes the code but checks nothing!
    $this->assertTrue(true); // 100% line coverage, 0% mutation score
}
✓ Fixed
// Meaningful coverage with real assertions:
public function testCalculateTotalAppliesVat(): void {
    $cart = new Cart(new VatCalculator(0.20));
    $cart->add(new Item('Widget', 10.00));
    $this->assertSame(12.00, $cart->calculateTotal(), '20% VAT not applied');
}
// Run mutation testing:
// vendor/bin/infection --min-msi=80 --min-covered-msi=90

Added 16 Mar 2026
Edited 5 Apr 2026
Views 50
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 0 pings W 1 ping T 0 pings F 0 pings S 1 ping S 0 pings M 0 pings T 0 pings W 0 pings T 2 pings F 0 pings S 1 ping S 1 ping M 1 ping T 0 pings W 0 pings T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 2 pings W 1 ping T 0 pings F 0 pings S 1 ping S 1 ping M 2 pings T 0 pings W
No pings yet today
PetalBot 2
Amazonbot 9 Perplexity 7 SEMrush 5 Ahrefs 4 Scrapy 4 PetalBot 4 Bing 3 Unknown AI 2 ChatGPT 2 Google 1 Claude 1 Meta AI 1
crawler 40 crawler_json 3
DEV INTEL Tools & Severity
🟡 Medium ⚙ Fix effort: Medium
⚡ Quick Fix
Use branch coverage (not line coverage) as your primary metric — line coverage misses untested if/else branches; mutation testing reveals if your assertions actually verify behaviour
📦 Applies To
PHP 7.0+ any web cli queue-worker
🔗 Prerequisites
🔍 Detection Hints
100% line coverage but critical branches untested; coverage met by running code not asserting results; no mutation testing to verify coverage quality
Auto-detectable: ✓ Yes phpunit xdebug pcov infection
⚠ Related Problems
🤖 AI Agent
Confidence: Medium False Positives: Medium ✗ Manual fix Fix: Medium Context: File Tests: Update


✓ schema.org compliant