Skip to content

Code Coverage — Statement, Branch, Path Coverage (2026)

DodaTech Updated 2026-06-20 8 min read

In this tutorial, you'll learn about Code Coverage. We cover key concepts, practical examples, and best practices.

Code coverage is a metric that measures how much of your source code is executed during testing — reported as percentages for statements, branches, functions, and lines covered.

What You'll Learn

You'll understand statement, branch, function, line, and path coverage metrics, use Istanbul/nyc for JavaScript projects, set meaningful coverage thresholds, and learn why 100% coverage doesn't guarantee bug-free code.

Why Code Coverage Matters

Coverage metrics reveal untested code paths. If a function has 50% branch coverage, half of its decision points have never been exercised. This helps prioritize testing effort. At DodaTech, DodaZIP's compression algorithms target 95%+ branch coverage — compression edge cases can corrupt archives.

Code Coverage Learning Path

flowchart LR
  A[Unit Testing Guide] --> B[Code Coverage]
  B --> C[Mutation Testing]
  B --> D[Quality Gates in CI]
  style B fill:#f90,color:#fff

Coverage Types

Type What It Measures Example
Statement Each statement executed Every line of code runs
Branch Each decision outcome Both if and else paths
Function Each function called Every method invoked
Line Each line of code executed Similar to statement
Path Every possible route through code All combinations of conditions

Statement Coverage

Measures how many executable statements are executed.

function classifyAge(age) {
  if (age >= 18) {        // Statement 1
    return 'adult';       // Statement 2
  }
  return 'minor';         // Statement 3
}

// Test only with age >= 18
test('returns adult for 18+', () => {
  expect(classifyAge(20)).toBe('adult');
});
// Statement coverage: 66% (2 of 3 statements)

Branch Coverage

Measures how many decision outcomes are tested.

test('returns adult for 18+', () => {
  expect(classifyAge(20)).toBe('adult');
});

test('returns minor for under 18', () => {
  expect(classifyAge(15)).toBe('minor');
});
// Branch coverage: 100% (both if/else paths covered)

Path Coverage

Measures all possible paths through the code.

function discount(price, isMember, hasCoupon) {
  let final = price;
  if (isMember) final *= 0.9;       // Path decision 1
  if (hasCoupon) final *= 0.95;     // Path decision 2
  return final;
}

// Paths: (not member, no coupon), (member, no coupon),
//        (not member, coupon), (member, coupon)
// Requires 4 tests for 100% path coverage

Using Istanbul/nyc

Istanbul is the standard JavaScript code coverage tool. nyc is its CLI.

Setup

npm install --save-dev nyc
// package.json
{
  "scripts": {
    "test": "nyc mocha",
    "coverage": "nyc --reporter=html --reporter=text mocha"
  }
}
npm run coverage

Expected output:

----------- --------- --------- --------- --------- -----------
----------- --------- --------- --------- --------- -----------
math.js 94.44 75 100 94.44 10-11
service.js 80 50 66.67 80 15,22-25
----------- --------- --------- --------- --------- -----------
All files 88.23 66.67 85.71 88.23

### Configuration

```json
// .nycrc.json
{
  "all": true,
  "include": ["src/**/*.js"],
  "exclude": ["src/**/*.test.js"],
  "reporter": ["text", "html", "lcov"],
  "branches": 80,
  "lines": 85,
  "functions": 85,
  "statements": 85
}

Jest Built-in Coverage

npx jest --coverage

Configure in jest.config.js:

module.exports = {
  collectCoverage: true,
  collectCoverageFrom: ['src/**/*.{js,jsx}', '!src/**/*.test.{js,jsx}'],
  coverageThreshold: {
    global: {
      branches: 80,
      functions: 80,
      lines: 85,
      statements: 85,
    },
  },
};

Setting Coverage Thresholds

Project Type Statement Branch Function
Library/Package 95% 90% 95%
Backend API 85% 80% 85%
Frontend SPA 80% 75% 80%
Prototype/MVP 60% 50% 60%

Enforcing Thresholds in CI

# <a href="/devops/github-actions/">GitHub Actions</a> — enforce coverage
- run: npx jest --coverage --coverageThreshold='{"global":{"branches":80}}'
- name: Check coverage
  run: |
    if [ $(npx jest --coverage --silent | grep 'Branches' | awk '{print $4}' | tr -d '%') -lt 80 ]; then
      echo "Branch coverage below 80%"
      exit 1
    fi

Why 100% Coverage Doesn't Guarantee Quality

High coverage is valuable, but it's not the whole picture.

function add(a, b) {
  return a + b;
}

// 100% coverage, but zero value
test('add function works', () => {
  expect(add(1, 2)).toBe(3);  // What about strings? Nulls? Missing args?
});

Coverage Blind Spots

What Coverage Misses Example
Missing logic Tested function but missed wrong implementation
Concurrency bugs All paths covered, but race condition exists
Security flaws Both branches tested, but input validation missing
Performance issues Code executes, but is 100x slower than expected
Integration bugs Each unit covered individually, but they don't work together

Goodhart's Law

"When a metric becomes a target, it ceases to be a good metric." Teams optimizing for 100% coverage write tests that hit every line but verify nothing meaningful.

Best Practices

1. Focus on Branch Coverage

Branch coverage reveals untested decision paths. It's more valuable than line coverage.

2. Set Thresholds, But Don't Worship Them

Use thresholds as a floor, not a target. A PR that increases coverage by 2% is valuable even if below threshold.

3. Review Uncovered Lines

The uncovered lines report tells you exactly what's not tested. Prioritize critical paths.

npx istanbul report html
# Open coverage/index.html — red lines are uncovered

4. Don't Test Getters/Setters/Boilerplate

Trivial code that's unlikely to break doesn't need tests. Focus on logic.

5. Combine with Mutation Testing

Coverage measures what code runs. Mutation testing measures whether the tests actually verify behavior.

Common Mistakes

1. Gaming the Metric

Writing tests that execute code but assert nothing. Coverage tools don't check assertion quality.

2. Ignoring Branch Coverage

75% branch coverage means 25% of decisions are never tested. That's where bugs hide.

3. 100% Coverage as a Gate

Teams waste time testing trivial code while missing critical untested paths.

4. Coverage Without Review

Looking at the number without inspecting which lines are uncovered.

5. Not Running Coverage in CI

Coverage is only useful if it's measured consistently. Local runs differ from CI.

6. Ignoring Integration Coverage

Unit test coverage gives a false sense of safety if integration points are untested.

7. Too Low or Too High Thresholds

50% is too low to be meaningful. 100% encourages gaming. 80-90% is the sweet spot.

Practice Questions

1. What are the five types of code coverage? Statement, branch, function, line, and path coverage.

2. What is the difference between statement and branch coverage? Statement coverage measures whether each line executes. Branch coverage measures whether each decision outcome (if/else) is tested.

3. Why is 100% coverage not enough? 100% coverage means code runs, not that behavior is verified. Missing assertions, wrong logic, concurrency bugs, and integration issues aren't caught by coverage metrics.

4. What tool is used for JavaScript code coverage? Istanbul/nyc is the standard. Jest has built-in coverage via --coverage.

5. Challenge: Improve coverage on a module without adding value. Take a module with 100% statement coverage but no meaningful assertions. Rewrite the tests to verify behavior, not just execution.

Mini Project: Coverage Report Analyzer

// coverage-analyzer.js
class CoverageAnalyzer {
  constructor(report) {
    this.report = report;
  }

  getUncoveredBranches() {
    const uncovered = [];
    for (const [file, data] of Object.entries(this.report)) {
      if (data.branchCoverage < 100) {
        data.branches.forEach((branch, index) => {
          if (branch.count === 0) {
            uncovered.push({
              file,
              branch: index,
              line: branch.line,
              type: branch.type,
            });
          }
        });
      }
    }
    return uncovered;
  }

  suggestPrioritization() {
    const uncovered = this.getUncoveredBranches();
    return uncovered
      .filter(b => b.type === 'if' || b.type === 'switch')
      .map(b => `Prioritize: ${b.file}:${b.line} — untested conditional`);
  }

  getScore() {
    const files = Object.values(this.report);
    const avg = (key) =>
      files.reduce((s, f) => s + f[key], 0) / files.length;
    return {
      statements: avg('statementCoverage'),
      branches: avg('branchCoverage'),
      functions: avg('functionCoverage'),
      lines: avg('lineCoverage'),
    };
  }
}

const report = {
  'math.js': {
    statementCoverage: 94,
    branchCoverage: 75,
    functionCoverage: 100,
    lineCoverage: 94,
    branches: [
      { line: 10, count: 5, type: 'if' },
      { line: 12, count: 0, type: 'else' },
    ],
  },
};

const analyzer = new CoverageAnalyzer(report);
console.log(analyzer.getScore());
console.log(analyzer.suggestPrioritization());

FAQ

What is code coverage?

A metric that measures how much of your source code is executed during testing, reported as percentages for statements, branches, functions, and lines.

What is the difference between Istanbul and nyc?

Istanbul is the coverage instrumenter. nyc is the CLI that runs Istanbul, collects results, and generates reports.

What is a good coverage threshold?

80-90% for most projects. Libraries should aim higher (95%+), prototypes can be lower (60%+). Branch coverage is more important than line coverage.

Does 100% coverage mean my code is bug-free?

No. Coverage measures which code ran, not whether the tests verified correct behavior. You can have 100% coverage with zero meaningful assertions.

How do I measure coverage for frontend applications?

Jest includes built-in coverage via --coverage. For Cypress E2E tests, use the @<a href="/testing-qa/cypress/">cypress</a>/code-coverage plugin.

What's Next

Mutation Testing — Testing Your Tests
CI/CD Testing Pipeline — Automating Tests in CI
Unit Testing Guide — Best Practices

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro