Code Coverage
debt(d5/e3/b5/t7)
Closest to 'specialist tool catches it' (d5). Tools listed — phpunit, xdebug, pcov, infection, codecov — are specialist coverage and mutation-testing tools that can surface untested paths and meaningless assertions. However, the core misuse (tests that execute lines without asserting behaviour) is not caught by a default linter; it requires deliberate use of mutation testing (Infection) or careful review of test quality, placing it squarely in specialist-tool territory rather than compiler or default-linter detection.
Closest to 'simple parameterised fix' (e3). The quick_fix indicates adopting PHPUnit with Xdebug/PCOV and adding mutation testing via Infection — a modest tooling and configuration change. However, fixing the underlying common mistake (meaningless tests written to hit a coverage target) requires rewriting those tests to include meaningful assertions, which is more than a one-liner but typically contained within the test suite rather than spanning the entire codebase.
Closest to 'persistent productivity tax' (b5). Coverage targets and metrics apply across web, cli, and queue-worker contexts (per applies_to) and influence every test-writing decision across the project. A bad coverage culture (chasing 100%, ignoring mutation testing) persistently slows down meaningful QA work and misleads teams about code quality, affecting many work streams without necessarily being architecturally load-bearing.
Closest to 'serious trap' (t7). The misconception field is explicit: '100% code coverage means the code is fully tested' — a deeply held wrong belief that contradicts how the metric actually works. Coverage measures line execution, not correctness of assertions. This contradicts how developers reason about testing in other contexts (e.g. passing tests imply correct behaviour), making it a serious trap that experienced developers across ecosystems commonly fall into.
Also Known As
TL;DR
Explanation
Code coverage (line, branch, path, mutation) measures which code was executed during tests. PHPUnit integrates with Xdebug or PCOV to generate coverage reports. High coverage does not guarantee correctness — tests can execute code without asserting meaningful outcomes. Mutation testing (Infection PHP) goes further, introducing bugs into the code and checking whether tests fail. Rather than chasing 100% line coverage, focus coverage efforts on business logic, security-sensitive code, and complex conditionals where untested branches pose real risk.
Diagram
flowchart TD
subgraph Coverage_Types
LINE[Line coverage<br/>was each line executed]
BRANCH[Branch coverage<br/>was each if/else path taken]
MUT2[Mutation coverage<br/>do tests catch changes]
end
subgraph Pitfalls
FALSE_100[100pct line coverage<br/>but no assertions<br/>tests nothing]
METRIC[Coverage as a target<br/>Goodharts Law - gaming it]
end
subgraph Better_Goal
MEANINGFUL[Meaningful assertions<br/>test behaviours not lines]
MUTATION[High mutation score<br/>tests actually catch bugs]
end
style FALSE_100 fill:#f85149,color:#fff
style METRIC fill:#f85149,color:#fff
style MEANINGFUL fill:#238636,color:#fff
style MUTATION fill:#238636,color:#fff
Common Misconception
Why It Matters
Common Mistakes
- Setting 100% coverage as a target — teams write meaningless tests just to hit the number.
- Counting coverage on generated code, migrations, and config files — only measure what matters.
- Using line coverage instead of branch coverage — a line with an if can be covered without testing the false path.
- Treating coverage as sufficient QA — coverage measures quantity of testing, not quality.
Code Examples
// Test that covers code without testing behaviour:
public function testUserCreation(): void {
$user = new User('Alice', 'alice@example.com');
$this->assertInstanceOf(User::class, $user); // Just checks the object was created
// 100% line coverage — zero meaningful assertion about User's behaviour
}
# phpunit.xml — require 80% line coverage on CI
<coverage>
<report>
<clover outputFile='build/coverage.xml'/>
<html outputDirectory='build/coverage-html'/>
</report>
</coverage>
# Enforce minimum in CI
$ phpunit --coverage-clover=coverage.xml
$ php vendor/bin/coverage-check coverage.xml 80