Mesrai
All guides
Learn · Code coverage

Code coverage: what it is, how it's measured, and how it differs from test coverage

Updated · Mesrai editorial team

Short answer

Code coverage is a measure of how much of a program's source code is executed when its automated tests run, usually reported as a percentage of lines, branches or functions. It is calculated by instrumenting the code and recording what runs: covered items ÷ total items × 100. Test coverage is a broader measure of how much of the required behaviour and risk the tests actually check. High code coverage shows code ran during tests — not that it was verified — so treat it as a floor, not a goal.

What is code coverage?

Code coverage (sometimes called test code coverage) answers one question: when the test suite runs, which parts of the code are executed, and which are never touched? A coverage tool records this while the tests run and produces a report — a percentage for the whole project, per file, and often a line-by-line view that highlights untested code.

Its value is in what it reveals about the gaps. Code that no test executes can't be protected by tests: if it breaks, nothing fails. A coverage report is the fastest way to find those blind spots — an error-handling branch never exercised, a feature flag path nobody tested, a module added without tests.

How code coverage is calculated

Coverage tools instrument the code — either by rewriting it before the tests run (as Istanbul does for JavaScript) or by using hooks in the runtime (as coverage.py and V8 coverage do). Each line, branch or function gets a counter. After the run, the tool compares what executed with what exists.

The formula is the same for every metric: covered items divided by total items, multiplied by 100. If a file has 200 executable lines and the tests execute 150 of them, line coverage is 150 ÷ 200 × 100 = 75%. If the same file has 40 decision outcomes (each if has two: true and false) and the tests take 24 of them, branch coverage is 60% — lower, because tests often skip the error paths.

Code coverage metrics: line, branch, function and more

Different metrics count different things. Line coverage is the number most tools show first, but branch coverage is the more honest one, because it catches untested else-branches and error paths that line coverage reports as covered.

Common code coverage metrics, from weakest to strongest
MetricWhat it countsCatches
Function coverageFunctions called at least onceEntirely untested functions
Line / statement coverageExecutable lines or statements runUntested blocks of code
Branch coverageBoth outcomes of every decision (if/else, switch cases, ternaries)Untested error paths and else-branches
Condition coverageEvery boolean sub-expression evaluated true and falseConditions that never change the result in tests
MC/DCEach condition shown to independently affect the outcomeRequired for the most critical avionics software (DO-178C Level A)

What is test coverage?

Test coverage asks a different question: of the things this software must do, how many have a test that would fail if they broke? It is measured against a list of requirements, user flows, business rules or risks — not against lines of code — so it can't be produced automatically by a tool, but it is much closer to what matters to users.

Common test coverage techniques include:

  • Requirements coverage. Map every requirement or acceptance criterion to at least one test (a traceability matrix).
  • Risk-based coverage. Test most heavily where failure would cost most: payments, permissions, data deletion, migrations.
  • Boundary value analysis. Test at and around the edges of valid input — 0, 1, the maximum, one past the maximum.
  • Equivalence partitioning. Group inputs that should behave the same and test one value from each group.
  • User-journey coverage. End-to-end tests for the critical paths users take through the product.

Code coverage vs test coverage

The two are complementary. Code coverage tells you which code no test touches; test coverage tells you whether the behaviour that matters is actually checked. A project can have 95% code coverage and still miss a requirement entirely, or have every requirement tested while large areas of code are never executed.

Code coverageTest coverage
MeasuresCode executed by testsRequirements and risks checked by tests
UnitLines, branches, functionsRequirements, user flows, risks
How it's producedAutomatically, by a coverage toolManually, by mapping tests to requirements
AnswersWhich code is never tested?Is the behaviour that matters verified?
Main weaknessExecution isn't verificationDepends on the requirement list being complete

Why 100% code coverage can still miss bugs

Coverage records execution, not assertions. The first test below runs every line and both branches of the function — 100% line and branch coverage — yet it would pass even if the discount were wrong, because it checks nothing. The second test has the same coverage and actually verifies the behaviour.

Same coverage, very different protection
function finalPrice(price: number, isMember: boolean) {
  return isMember ? price * 0.9 : price;
}

test("finalPrice runs", () => {
  finalPrice(100, true);
  finalPrice(100, false); // 100% lines and branches — nothing asserted
});

test("members get 10% off", () => {
  expect(finalPrice(100, true)).toBe(90);
  expect(finalPrice(100, false)).toBe(100);
});

What is a good code coverage percentage?

There is no universal number. Google's published guidance on code coverage describes roughly 60% as acceptable, 75% as commendable and 90% as exemplary — and warns against treating any threshold as the goal in itself. Many teams settle around 70–80% overall, with higher expectations for critical code such as billing, permissions and data handling.

Chasing the last few percent is rarely worth it: the remaining code is often trivial getters, generated code or hard-to-reach failure handling, and tests written only to raise the number tend to assert little. A falling coverage trend is usually more informative than the absolute value.

Code coverage tools by language

Every mainstream language has a mature coverage tool, and most CI services and code-quality platforms can read their reports.

LanguageCoverage toolNotes
JavaScript / TypeScriptIstanbul (nyc), V8 coverage (c8)Built into Jest and Vitest
Pythoncoverage.pyUsually run through pytest-cov
Java / KotlinJaCoCoMaven and Gradle plugins
Gogo test -coverBuilt into the Go toolchain
C / C++gcov, llvm-covCompiler-based instrumentation
.NET (C#)CoverletWorks with dotnet test
RubySimpleCovCommon with RSpec and Minitest
PHPPHPUnit with Xdebug or PCOVPCOV is faster for coverage only
SwiftXcode code coverageEnabled per test scheme

Measuring code coverage in CI

Run coverage on every pull request and fail the build when it drops below a threshold. Services such as Codecov and Coveralls, and platforms such as SonarQube, import the reports to show coverage trends and the coverage of each pull request's changes — but they don't measure coverage themselves; your test runner does.

Enforcing a minimum in three ecosystems
# Jest (jest.config.js)
coverageThreshold: { global: { branches: 80, functions: 80, lines: 80, statements: 80 } }

# Python
pytest --cov=myapp --cov-fail-under=80

# Go
go test -coverprofile=coverage.out ./...
go tool cover -func=coverage.out

Better than one global number: diff coverage and mutation testing

Diff coverage (also called patch coverage) requires that new and changed lines in a pull request are covered. It stops coverage from eroding without forcing anyone to retrofit tests onto old code, and it puts the conversation where it belongs — on the change being reviewed.

Mutation testing measures whether tests would actually catch a bug. Tools such as Stryker (JavaScript, C#), PIT (Java) and mutmut (Python) make small changes to the code — flip a condition, change a constant — and check that at least one test fails. A surviving mutant points at code that is executed but not verified, exactly the gap line coverage hides.

Reviewing tests in pull requests

Coverage reports tell reviewers where tests are missing; they don't say whether the tests are good. In code review, check that tests assert on outcomes, that the failure paths the change introduces are tested, and that the tests would fail if the behaviour described in the ticket broke. Watch for tests that were deleted or skipped to make the build pass, and for mocks that replace the very behaviour under test.

Code coverage review checklist

  • Every new or changed behaviour has a test that would fail if it broke.
  • Tests assert on results, not just that code runs without throwing.
  • Error and edge-case branches added by the change are tested.
  • Diff coverage on the pull request meets the team threshold.
  • Branch coverage, not only line coverage, is tracked.
  • Critical paths — payments, permissions, data deletion — have stronger coverage.
  • No tests were deleted or skipped to make the build pass.
  • Mocks don't hide the behaviour the test is meant to check.
// mesrai

How Mesrai helps with test coverage

Mesrai reviews the tests in a pull request alongside the code: it points out changed behaviour with no test, tests that assert nothing, and error paths left untested. Teams can make expectations explicit with Mesrai Rules — for example, requiring tests for every change under a payments directory.

  • Flags untested behaviour and assertion-free tests
  • Coverage expectations as Mesrai Rules, scoped by path
  • Business logic validation against the linked ticket
  • Comments on the pull request where the gap is

Sources

Further reading

?

Code coverage — frequently asked

9 questions
  • What is code coverage in simple terms?

    +

    The percentage of your code that runs when your automated tests run. If tests execute 750 of 1,000 lines, line coverage is 75%.

  • How is code coverage calculated?

    +

    Covered items divided by total items, times 100. A coverage tool instruments the code, records which lines, branches or functions execute during the tests, and reports the ratio.

  • What is the difference between test coverage and code coverage?

    +

    Code coverage measures how much of the code runs during tests. Test coverage measures how much of the required behaviour and risk the tests check. Code coverage is produced automatically; test coverage is judged against requirements.

  • What is a good code coverage percentage?

    +

    Many teams aim for 70–80% overall, with higher coverage on critical code. Google's guidance calls about 60% acceptable, 75% commendable and 90% exemplary. Requiring coverage on changed lines usually works better than a single global target.

  • Is 100% code coverage worth it?

    +

    Rarely. The last few percent are often trivial or hard-to-reach code, and 100% execution doesn't mean the behaviour is verified. Effort is better spent on strong assertions and critical paths.

  • Is branch coverage better than line coverage?

    +

    Yes. Branch coverage requires both outcomes of each decision to be exercised, so it exposes untested error and else-paths that line coverage can report as covered.

  • What are the best code coverage tools?

    +

    Use the standard tool for your language: Istanbul or V8 coverage for JavaScript and TypeScript, coverage.py for Python, JaCoCo for Java, go test -cover for Go, Coverlet for .NET and SimpleCov for Ruby. Codecov, Coveralls and SonarQube can import their reports to track trends.

  • What is unit test coverage?

    +

    Code coverage measured while running only the unit tests. It is usually lower than coverage from the full suite and shows how much logic is protected by fast, isolated tests.

  • What is mutation testing?

    +

    A technique that makes small changes to the code and checks that at least one test fails. It measures whether tests actually detect wrong behaviour, which coverage alone cannot.