Coverage·Field Guide
DARK
SE401 · Software Testing and Quality · Chapter 03

A green report doesn't mean
a correct program.

Code coverage tells you which lines your tests ran — never which lines your tests actually proved correct. It's a flashlight for finding untested code, not a certificate of quality. This chapter is the case for using the number honestly: chase the gaps it reveals, ignore the temptation to worship the percentage itself.

Statements · Branches · Functions · Lines
Coverage ≠ Quality
Google's 60/75/90 scale
Exam-relevant
COVERAGE REPORT line 1 — covered line 2 — covered line 3 — NOT covered line 4 — covered line 5 — NOT covered 60% executed, not proven correct statements · branches · functions · lines
§ 01 — THE DEFINITION

What code coverage actually measures

Lock in the exact definition first — it's the anchor for every argument in this chapter.

🧭
Memory hook
"Buses Stop For Lines" — B, S, F, L
A coverage report's percentage is formed through Branches, Statements, Functions, and Lines of code covered by a test suite — the exact four ingredients named in the slides, in that order.
Definition

The percentage of your codebase exercised by tests

Code coverage identifies the percentage of your codebase that was exercised — meaning executed, not necessarily verified — by a test suite. That percentage is what gets displayed in a coverage report.

Why teams track it

It's often a gate, not just a diagnostic

On product delivery teams, developers are frequently required to back the code they write with unit/integration/e2e tests. Some teams go further, requiring every incoming branch to meet a coverage threshold — trusting (often wrongly) that a high percentage eliminates bugs from the production codebase.

Three general remarks worth memorizing verbatim: (1) Developers tend to do just enough to hit the number. (2) Coverage does not ensure quality. (3) High code coverage should never remove the need for a code review.
§ 02 — THE CORE TRAP

Coverage is helpful. Chasing the percentage is not.

This is the single idea the whole deck circles back to — get it airtight.

🎭
Memory hook
"Covered ≠ Correct"
The coverage report is genuinely helpful for identifying untested code — but targeting a percentage of coverage does not equal quality-tested code. Code coverage itself isn't bad; it just provides a false sense of security when treated as a finish line instead of a flashlight.
Slide-verbatim summary: "A high coverage percentage does not equal quality-tested code" and "a target coverage threshold should not take the place of code reviews." Instead of chasing the percentage, developers should understand — and experience — the value of well-formed tests. (TDD, by definition, should let developers attain high coverage percentages as a side effect, not as the goal itself.)
§ 03 — WHERE COVERAGE ACTUALLY HELPS

The real, practical benefits

🔦

Finds untested code

Helps developers see what portions of the codebase are covered by unit tests — and, just as usefully, what isn't.

🗺️

Orients you on a new project

The first thing to do when rolling onto an existing project: run a coverage report to see where testing focus should go.

🎯

Pinpoints weak files/components

Reports break the percentage down by branches, statements, and functions, making it easy to spot which files or components lack tests.

🚦

Automates CI/CD gating

A pipeline can automatically fail a branch with low coverage, keeping the production branch purer.

🔁

Falls out of TDD naturally

Teams practicing Test-Driven Development should sit around 80%-90% coverage automatically — though language/framework quirks can keep even TDD teams from hitting a "clean" 100%.

🤝

Frames the client conversation

Some clients want a high coverage percentage — that's fine. As developers/consultants, the job is helping them understand what an application solution actually needs.

§ 04 — WRITING TESTS THE RIGHT WAY ROUND

Spec first, coverage second

The order of operations here is exactly the kind of thing an exam question will try to flip on you.

📋
Memory hook
"Spec writes the tests. Coverage checks your memory."
Your initial set of tests should be derived from the program's specification — not from the coverage report. Coverage only comes in afterward, as a "did I forget something?" sanity check.
Step 1 — derive tests from the spec
Ask: what does the program need to do? Are there corner cases implied by the requirements? Are there special cases the program needs to handle? Build your initial test set from those answers, not from a coverage target.
Step 2 — use coverage as a memory check
Once you've engineered every test you could think of, run coverage to see "if you forgot something." If the report flags a missed branch, ask yourself: "why didn't I see this when I was creating the tests?" Either you simply forgot (good — that's coverage proving its value) or you had a deliberate reason not to test it (also good — the report made you reflect on that choice).
§ 05 — GOOGLE'S CODE COVERAGE BEST PRACTICES

The deep-dive: nine rules from the industry playbook

Code coverage is a "surprisingly polarizing topic" among engineers — these rules exist to move the conversation past the arguing and into using coverage pragmatically.

1 · A useful metric — but an indirect one

benefits & limits

Coverage isn't a perfect measure of test quality, but it is a reasonable, objective, industry-standard metric with actionable data. It needs little human interaction, applies universally across products, and has ample tooling for most languages.

The catch: it's an indirect metric that compresses a huge amount of information into a single number — so it should never be your only source of truth. Use it alongside other techniques for a more holistic view of your testing effort.

2 · Culture change, not a guaranteed defect cure

open research question

Whether coverage alone reduces defects is genuinely an open research question. But experience shows that efforts to increase coverage often trigger culture changes that do reduce defects in the long run.

The chain reaction: teams that prioritize coverage treat testing as a first-class citizen → they bake stronger testability into product design → that leads to higher-quality code (more modular, cleaner API contracts, more manageable reviews) → teams start caring more about overall engineering and operational health.

3 · High % ≠ high quality

the false-security trap

Chasing 100% leads to a false sense of security, and can be wasteful — burning machine cycles and creating technical debt from low-value tests that now need maintenance.

Two ways bad code still ships
(a) tests didn't cover a specific code path — a gap that's easy to catch with coverage analysis. (b) tests didn't cover a specific edge case in an area that did have coverage — difficult or impossible to catch with coverage analysis alone.
The fix for (b)
Coverage only guarantees a line/branch was executed by a test — never that it was tested correctly. Watch for tests copy-pasted just to inflate the number. A better technique for checking whether covered lines are meaningfully exercised: mutation testing.

4 · Low % does guarantee a problem

the flip side

While high coverage doesn't guarantee quality, a low coverage number does guarantee that large areas of the product go completely untested by automation on every deployment — raising the real risk of pushing bad code to production.

The headline rule: a lot of the value of coverage data is in highlighting what's not covered, not what is.

5 · No universal "ideal number"

context-dependent target

There is no single coverage percentage that's right for every product. The right level of testing for a piece of code should be a function of three things.

⚖️
Memory hook — the 3 R's
"Risk, Rate, Runway"
Risk — business impact/criticality of the code · Rate — how often you'll need to touch/change it · Runway — how much longer you expect it to live, plus its complexity and domain variables.
You can't mandate one x% for every team — it's a business decision best made by the product's owners, who have domain-specific knowledge. Any mandate should come bundled with infrastructure investment (tools baked into the developer workflow) — otherwise engineers start treating the target like a checkbox and avoid going beyond it, even when it would be prudent to.

6 · Google's general guideline scale

60 / 75 / 90

In general, coverage across a lot of products is below the bar — the aim should be significant improvement across the board. While there's no single "ideal" number, Google offers general guidelines:

60%75%90%
60%
"Acceptable"
75%
"Commendable"
90%
"Exemplary"
Google explicitly avoids broad top-down mandates — every team is encouraged to select the value that fits its own business needs.

7 · Diminishing returns near the top

90% → 95% isn't worth it

Don't obsess over squeezing 90% coverage up to 95% — the gains of increasing coverage beyond a certain point are logarithmic. The better use of effort is taking concrete steps to move a weak area from 30% to 70%, and making sure new code always meets the team's desired threshold.

8 · Human judgment beats the raw number

code review integration

More important than the percentage of lines covered is human judgment over the actual lines of code (and behaviors) that aren't covered — analyzing the gaps and deciding whether that risk is acceptable. What's not covered is more meaningful than what is.

Embed it in code review
Pragmatic discussions over specific uncovered lines during code review are more valuable than over-indexing on an arbitrary target number. Embedding coverage data into the review process has been found to make reviews faster and easier.
Not all code is equally important
Testing a debug log line usually matters less than testing core logic. Seeing each covered line highlighted directly in the review — not just the aggregate number — helps developers make sure the important code is what's actually covered.

9 · Legacy code, frequency, scope, and gating

the operational rules

Four closely related closing rules, all about applying coverage in the real world.

Legacy code — the boy-scout rule
Inheriting a poorly-tested legacy system is daunting and you may not know where to start. At minimum, adopt the boy-scout rule: "leave the campground cleaner than you found it." Incremental, concrete improvements get you to a healthy codebase over time.
Frequently changing code
Project-wide goals above 90% are usually not worth chasing, but per-commit coverage goals of 99% are reasonable, with 90% as a good lower threshold. The point: make sure your tests aren't quietly getting worse over time.
Unit coverage is only one piece
Integration/system test coverage matters too — the aggregate view across your whole pipeline (unit + integration) is what gives you the real picture of what's not exercised on the way to production. Caveat: unit-test coverage correlates tightly with what's actually evaluated, while integration/e2e coverage is often incidental, not deliberate — but including it avoids a false sense of security where you think integration tests cover code that unit tests don't.
Gate deployments — carefully
Teams should debate and choose their own gating mechanism: gate on all code vs. new code only; a hard-coded number vs. delta from the prior version; specific areas to ignore or focus on. Whatever is chosen, commit to it as a team — a coverage drop that violates the gate should block the code from being checked in or reaching production. Just don't let the gate become a rubber-stamp checkbox: pressure to "hit the metric" almost never produces the outcome you actually want.
§ 06 — CHEAT SHEET

Every number and rule, in one table

ConceptRule / Number
Coverage report is built fromBranches, Statements, Functions, Lines
TDD teams naturally land around80%–90% coverage
Google — "acceptable"60%
Google — "commendable"75%
Google — "exemplary"90%
Reasonable per-commit goal99% (90% as lower threshold)
Returns beyond ~90%Logarithmic — not worth obsessing over
Better focus than 90%→95%Move weak areas from 30%→70%
Technique for "covered but not tested correctly"Mutation testing
Minimum move on inherited legacy codeBoy-scout rule (leave it cleaner than found)
3 factors that set the "right" targetBusiness impact/risk · change frequency · lifespan & complexity
What matters most in a gapHuman judgment on what's not covered, in code review
§ 07 — COMMON MISTAKES

Where students (and teams) actually get this wrong

These are the exact mix-ups this chapter is built to prevent.

1. Equating high coverage with high quality
✗ WRONG"100% coverage means the code is basically bug-free."
✓ RIGHTCoverage only shows a line was executed by a test — never that it was tested correctly. High % can even create a false sense of security.
2. Assuming low coverage is fine "because the important stuff is tested"
✗ WRONGLow coverage is a neutral, no-signal metric.
✓ RIGHTA low number does guarantee that large areas of the product ship completely untested by automation — that's a real, measurable risk.
3. Applying one "ideal" percentage to every team/project
✗ WRONG"Every team must hit 95% coverage, no exceptions."
✓ RIGHTThe right target is a business decision — a function of business impact, how often the code changes, and how long it will live.
4. Burning effort pushing 90% → 95%
✗ WRONGTreat every extra percentage point as equally valuable.
✓ RIGHTGains beyond ~90% are logarithmic. Spend that effort moving weak areas from 30% to 70% instead.
5. Letting a coverage target replace code review
✗ WRONG"We hit the threshold, so we can skip/rush the review."
✓ RIGHTCoverage should be embedded into the review process — human judgment about what's not covered is more valuable than the number itself.
6. Copy-pasting tests just to move the number
✗ WRONGAdd low-value or duplicated tests purely to hit a target.
✓ RIGHTThat inflates coverage without proving correctness — and adds technical debt. Use mutation testing to check whether covered lines are meaningfully exercised.
7. Tracking only unit-test coverage
✗ WRONGIgnore integration/e2e coverage entirely.
✓ RIGHTThe aggregate view across the whole pipeline (unit + integration) is what actually shows what's not exercised before production — unit-only tracking can hide a false sense of security.
8. Turning a coverage gate into a rigid checkbox
✗ WRONGEnforce the gate mechanically with no room for team judgment.
✓ RIGHTTeams should debate and choose their own gating mechanism and commit to it — pressure to "hit the metric" almost never yields the outcome you actually want.
§ 08 — POP QUIZ

Quick self-check

Click a question to reveal the answer.

Q1 · Multiple choice
A coverage report is formed through which four ingredients?
Answer: Branches, Statements, Functions, and Lines of code covered by a test suite.
Q2 · Short answer
Why does a high coverage percentage NOT guarantee high-quality test coverage?
Answer: Coverage only proves lines/branches were executed by a test, not that they were tested correctly — chasing 100% can create a false sense of security and waste effort on low-value tests (technical debt).
Q3 · Multiple choice
According to Google's general guidelines, what coverage level is considered "exemplary"?
Answer: 90% ("exemplary"). 60% is "acceptable" and 75% is "commendable."
Q4 · Short answer
What technique does the deck recommend for checking whether covered lines were actually tested correctly, not just executed?
Answer: Mutation testing.
Q5 · Short answer
What are the three factors that should determine the "right" coverage target for a piece of code?
Answer: (a) business impact/criticality of the code, (b) how often it will be touched/changed, (c) how much longer it's expected to live, its complexity, and domain variables.
Q6 · True/False
True or False: You should always push project-wide coverage as close to 100% as possible.
Answer: False. Gains beyond roughly 90% are logarithmic; project-wide goals above 90% are usually not worth it. Focus effort on moving weak areas (e.g. 30%→70%) instead, though per-commit goals near 99% are reasonable for new/changing code.
Q7 · Short answer
What's the recommended minimum action when you inherit a legacy system with poor test coverage?
Answer: Adopt the "boy-scout rule" — leave the campground cleaner than you found it. Improve coverage incrementally over time rather than trying to fix it all at once.
§ 09 — PRACTICE LAB

Apply the rules to realistic scenarios

Same shape of reasoning an exam question would ask for — grounded directly in the slide content.

1. Your team's coverage report shows 95%, yet bugs keep slipping into production. What is most likely happening, and what technique would help diagnose it?
→ The covered lines are being executed but not meaningfully tested (e.g. assertions are weak or copy-pasted). Use mutation testing to verify covered lines actually catch injected faults.
Why: coverage guarantees execution, not correctness — this is rule #3 from the Google playbook.
2. A manager wants to mandate 95% code coverage for every team in the company, with no exceptions. What's wrong with this plan, and what should replace it?
→ There is no universal "ideal number." The target should be a business decision made per-team based on business impact/risk, change frequency, and code lifespan — and any mandate needs infrastructure investment to avoid becoming a checkbox.
Why: rule #5 — one-size-fits-all mandates ignore domain-specific context and invite gaming of the metric.
3. You've just inherited a legacy module at 20% coverage that the team touches constantly. Coverage can't realistically jump to 90% this sprint. What's the practical move?
→ Apply the boy-scout rule — improve it incrementally with every change — and since it's frequently touched, aim for near-99% coverage on new/changed code going forward, even while the overall legacy number climbs slowly.
Why: combines the legacy-code rule with the "frequently changing code" rule — per-commit standards matter more than an instant project-wide fix.