Test Coverage
Test coverage is the degree to which your tests have exercised a defined set of things. The ISTQB glossary calls those things coverage items and expresses coverage as a percentage. Example: a user story has 8 acceptance criteria and 6 of them were checked by a test that ran on the current build, so acceptance-criteria coverage is 6 of 8, or 75%.
The first question is always "coverage of what?". Requirements, risks and code are covered and measured differently, usually by different people, and each view has its own blind spots. Coverage shows how much of a list was exercised; it never shows that the software is free of defects.
Why it matters
Without coverage, "we tested the feature" cannot be checked. Nobody can say which acceptance criteria have no test, which high risks were never tried, or which code no test has run. Those gaps are where defects slip into production.
For a tester, coverage turns effort into a statement others can verify and act on: "all 14 acceptance criteria have passing tests; 2 medium risks were not tested because the payment sandbox was down". It also makes exit criteria measurable and protects the team from false confidence in one large number.
Coverage of what? Requirements, risks and code
A coverage number means nothing until you say what was counted. Three kinds of coverage answer three different questions, and most teams need all three.
- Requirements coverage: which requirements or acceptance criteria have tests, and which have passing tests. The coverage items are the criteria in the test basis, counted through traceability: every test linked to the criterion it checks.
- Risk coverage: which identified product risks have tests that ran, and which have tests that passed, reported by risk level. "Both high risks tested and passing, 2 of 5 medium risks not tested yet" supports a release decision better than any single percentage.
- Code coverage: which parts of the code ran during the tests, measured by a tool. Statement coverage counts executable statements; branch coverage counts branches, the transfers of control in the code, which coverage tools report as decision outcomes, such as both sides of an
if. Branch coverage is stronger: 100% branch coverage includes 100% statement coverage, not the other way round.
Coverage items and how to measure them
The ISTQB Foundation syllabus v4.0 measures coverage through coverage items: countable things derived from test conditions by applying a test technique, as in the lesson on the test design activity. Test design identifies them; execution exercises them. The formula never changes: coverage = items exercised ÷ items identified × 100%.
Report the counts with the percentage ("9 of 12 criteria") and the build they apply to. In the ISTQB definition an item is covered once a test has exercised it, whatever the result, so for requirements and risks give two numbers: items with a test that ran on that build, and items whose tests passed. Only the second says the item works: a failed test leaves its item open, and a blocked or unrun test has exercised nothing yet. Requirements and risk coverage come from traceability links in a test management tool or matrix; code coverage comes from a tool that instruments the code (adds counters that record what runs) while the tests run, usually in the CI pipeline. What changes from one view or technique to the next is the item:
- Requirements: acceptance criteria or individual requirements.
- Risks: identified risks, grouped by risk level.
- Equivalence partitioning: partitions. Boundary value analysis: boundary values.
- Decision tables: the columns with feasible combinations of conditions.
- State transitions: states, valid transitions or all transitions.
- Code: executable statements, or branches (in coverage tools, each outcome of each decision).
Try it: Requirements → Test Cases Try it: State Transition Generator
Worked example: four coverage numbers for one feature
A checkout feature applies a discount code. Acceptance criteria: AC1, a valid code reduces the total by its percentage; AC2, an expired code is rejected with a clear message; AC3, codes apply only to totals of 100.00 SAR or more; AC4, one code per order. Product risks: R1 (high) a code is applied twice to one order; R2 (high) an expired code is accepted; R3 (medium) the 100.00 threshold is applied on the wrong side.
The developer wrote one unit test for the discount function; the tester ran three system tests on build 57, with TC-03 checking totals of 99.99 and 100.00. The code and reports are below. What each number says:
For code, an item counts as covered when it ran at least once during the passing unit test; for criteria and risks, when a linked system test passed on build 57. A failed test exercised its item but leaves it open.
- Statement coverage 100% (4 of 4): every statement of
applyDiscountran in the unit test, yet no unit test tried a total under 100.00. - Branch coverage 50% (1 of 2): the
falseoutcome oftotal >= 100never ran. A unit test with a total of 99.99 would cover it. - Acceptance criteria: 3 of 4 have a test (75%), but only 2 of 4 have a passing test (50%). TC-02 failed and AC4 has no test.
- Risks: 2 of 3 have a test, but only 1 of 3 has a passing test, and neither high risk does: R1 has no test and the test for R2 failed. This is the number the release decision needs.
- Nothing in
applyDiscounthandles "one code per order". If AC4 was never implemented, no code coverage report can show it: a tool measures only code that exists.
// discount.ts
export function applyDiscount(total: number, percent: number): number {
let result = total;
if (total >= 100) {
result = total - (total * percent) / 100;
}
return result;
}
// discount.test.ts (Jest): the only unit test
import { applyDiscount } from './discount';
test('10% off an order of 150', () => {
expect(applyDiscount(150, 10)).toBe(135);
});
Code coverage (unit tests) Statements Branches
discount.ts 4/4 100% 1/2 50%
Traceability, system tests on build 57
Criterion Test Result Risk
AC1 TC-01 passed -
AC2 TC-02 failed R2 (high)
AC3 TC-03 passed R3 (medium)
AC4 - no test R1 (high)Try it: Requirements → Test Cases Try it: Risk-Based Test Generator
Why 100% coverage does not mean bug-free
Coverage tells you how much of a list was exercised, not whether the list was complete, whether the tests checked the right things, or whether the software is correct. Two of the seven testing principles apply: testing shows the presence of defects, not their absence, and exhaustive testing is impossible.
- The denominator holds only what you identified. A missing requirement or an unlisted risk never appears in the percentage, so 100% of an incomplete list is still incomplete.
- Executing is not checking. A test with no assertion (no check of the result) that calls
applyDiscount(150, 10)gives the same 100% statement coverage as one that checks the result is 135. - Data matters.
applyDiscount(150, 110)returns a negative total, and full branch coverage would not reveal it. The syllabus's own example is a division that fails only when the divisor is zero. - Code coverage cannot see omissions: code that was never written has no statements to miss.
- A target can become a game: people add tests that run code without checking anything. Reviewing assertions, or mutation testing (small deliberate code changes that a good test suite must detect), helps show whether covered code is really checked.
Common mistakes
- Reporting "coverage is 85%" without saying of what, on which build and out of how many items.
- Reporting a criterion or a risk as done because a test exists, although that test failed, was blocked or has not run on the current build.
- Treating 100% code coverage as proof of correct code, or chasing the percentage with tests that check nothing.
- Forgetting that missing requirements and unlisted risks are not in the denominator, so no percentage can reveal them.
- Treating one shallow test as full coverage of a high-risk item: coverage counts items, not how thoroughly each one was tested.
The QA perspective
Coverage is planned, not discovered at the end. In test planning, agree with the test lead and the product owner which coverage criteria go into the exit criteria, for example: every acceptance criterion has a passing test, every high risk has passing tests, and the branch-coverage floor the developers set for changed code is met. Many teams put the first one into the Definition of Done too. In test design, link each test case to its criterion and risk, so coverage is computed from traceability, not estimated.
In reports, give the counts, the build and the gaps by name, grouped by risk level. Ask developers for the uncovered branches in changed code rather than one total, and ask the product owner whether anything is missing from the list, because no percentage shows that gap. At test completion, record what was deliberately not covered and why, so the release decision is made with the residual risk in view.
Practice exercise
Calculate and report coverage for a password reset story
Below are a user story, three risks, the system test results on build 112 and a unit test coverage report. Work out the requirements and risk coverage, decide what the code coverage figures do and do not tell you, and write the coverage part of the test progress report in three sentences for the product owner.
Story: Reset password by e-mail link
AC1 "Forgot password" sends a reset link to a registered e-mail address.
AC2 An unregistered address gets the same neutral message as a registered one.
AC3 The link expires 30 minutes after it is sent.
AC4 The link can be used only once.
AC5 The new password must be 12 to 64 characters long.
Risks
R1 AC3, AC4 high: a reset link can be reused or never expires
R2 AC2 high: the message reveals which addresses have accounts
R3 AC5 medium: the password rule is not enforced
System tests on build 112
TC-01 AC1 passed
TC-02 AC1 passed
TC-03 AC2 passed
TC-04 AC3 failed
TC-05 AC5 passed
Unit tests, reset-service.ts
statement coverage 96%
branch coverage 71%- Map each criterion to its tests and their results.
- Calculate criteria coverage twice: with any test that ran, and with a passing test.
- Calculate risk coverage, separately for high and medium risks.
- Interpret the unit test figures, then write the statement and your next actions.
Show the model answer
Criteria: AC1 (TC-01 and TC-02 passed), AC2 (TC-03 passed), AC3 (TC-04 failed), AC4 (no test), AC5 (TC-05 passed). With a test: 4 of 5 (80%). With a passing test: 3 of 5 (60%), the number that counts for the release.
Risks: R2 (high) and R3 (medium) have passing tests. R1 (high) does not: its expiry test failed and single use has no test. That is 2 of 3 risks, 1 of 1 medium risk, but only 1 of 2 high risks.
Code coverage: most of the code ran during the unit tests, but that does not make the expiry logic correct (TC-04 failed anyway), and it cannot show whether single-use logic exists at all. Ask which branches are uncovered and whether they belong to expiry or single use.
Statement: "Password reset, build 112: 3 of 5 acceptance criteria have a passing test (60%). High risk R1 is open: expiry test TC-04 failed and single use (AC4) is not tested yet; R2 and R3 have passing tests. Unit tests reach 96% statement and 71% branch coverage; the uncovered branches have not been reviewed yet."
Next actions: test AC4; report the TC-04 failure as a defect and retest after the fix (confirmation testing); add AC5 tests on both sides of each limit (11, 12, 64 and 65 characters), because one passing test covers the criterion by count, not in depth.
Frequently asked questions
Is test coverage the same as code coverage?
No. Test coverage is the general idea: the share of identified coverage items that the tests exercised. Code coverage is one kind of it, where the items are statements or branches. Requirements coverage, risk coverage and the coverage of a test technique (partitions, boundary values, transitions) are others.
What is a good code coverage percentage?
No number proves quality, and the ISTQB syllabus does not set one. Teams agree on a target as a safety net, often stricter for critical or changed code, and then read what is not covered instead of only the total. A high number with weak assertions is worth less than a lower one with strong checks.
Who measures code coverage, developers or testers?
Usually a coverage tool in the CI pipeline measures it while the automated tests run, most often the unit tests that developers write. Testers read the report, ask about uncovered parts in risky areas, and combine it with requirements and risk coverage, which testers usually own.
If we reach 100% of our coverage target, can we stop testing?
It means one exit criterion is met, not that the software has no defects. Check the other exit criteria, the residual risk and anything consciously left out. Even full coverage of the requirements does not prove that the product meets the needs of its users: that is the absence-of-defects fallacy.
References
- ISTQB Certified Tester Foundation Level (CTFL) v4.0The foundation syllabus of software testing: test levels and types, test design techniques, test management and tools.
- ISTQB GlossaryThe standard definitions of testing terms — test case, regression testing, severity, priority, boundary value analysis and the rest.
- ISO/IEC/IEEE 29119-1:2022 — Software and systems engineering — Software testing — Part 1: General conceptsThe international software-testing standard: concepts, processes, documentation and techniques.
Practise with Mutqan tools
Convert a list of numbered requirements into traceable test cases with a requirement-to-test matrix.
Derive test scenarios from functional requirements with coverage mapping.
Score features by likelihood and impact, then generate a prioritised risk-based test plan.
Define states and events, then generate a transition table, valid/invalid transition tests and 0-switch/1-switch coverage.
Compare requirements or a user story against existing test cases to find untested areas, with Mutqan AI.
Go deeper
Your progress on this topic
Not started
Mark the topic complete when you have studied it and done the exercise.
Sign in to track your progress
Mark topics complete, continue where you left off and unlock specialisation exams.