Skip to content

Karma Coverage Thresholds

Generating coverage reports is useful, but enforcing a minimum threshold turns coverage into an active CI gate that prevents regressions automatically. This lesson covers configuring karma-coverage's check option.

The check Option

coverageReporter.check lets you define minimum acceptable percentages for statements, branches, functions, and lines — globally across the whole project, and optionally per-file or per-directory. If actual coverage falls below any configured threshold, Karma exits with a non-zero code, failing the CI job.

coverageReporter: {
  dir: 'coverage/',
  reporters: [{ type: 'html' }, { type: 'text-summary' }],
  check: {
    global: {
      statements: 80,
      branches: 75,
      functions: 80,
      lines: 80
    }
  }
}

With this configuration, a karma start --single-run fails the process if project-wide coverage drops below any of these four thresholds.

Global vs. Per-File Thresholds

check: {
  global: {
    statements: 80,
    branches: 75,
    functions: 80,
    lines: 80
  },
  each: {
    statements: 60,
    branches: 50,
    functions: 60,
    lines: 60,
    overrides: {
      'src/legacy/**/*.js': {
        statements: 30
      }
    }
  }
}
  • global enforces one threshold across the entire aggregated codebase.
  • each enforces a (usually lower) minimum per individual file, catching files with disproportionately poor coverage.
  • overrides lets specific paths (like known legacy code) use different, typically lower, thresholds.
  • Failing a threshold check produces a clear terminal message naming exactly which metric and file/global scope failed.

Coverage Threshold Cheat Sheet

Key configuration keys for enforcing coverage in CI.

Key Scope
check.global One threshold for the whole aggregated codebase
check.each A threshold enforced per individual file
check.each.overrides Per-path threshold overrides (e.g. legacy code)
Any threshold miss Non-zero exit code, failing CI

Setting Realistic Starting Thresholds

When adding thresholds to an existing project for the first time, set them close to (but slightly below) current actual coverage, rather than an aspirational number like 90%. This turns the threshold into a genuine regression gate immediately, and you can raise it deliberately over time as coverage improves.

  • Run coverage once without thresholds to see current actual numbers.
  • Set initial thresholds a few points below current coverage, giving a small safety margin.
  • Raise thresholds gradually as the team genuinely improves coverage, not all at once.
  • Avoid setting thresholds so high that developers start writing meaningless tests just to pass CI.

Handling Legacy Code Exceptions Honestly

It's common for one specific legacy module to drag down otherwise healthy project-wide coverage. overrides lets you set an honest, lower threshold for that specific path without weakening standards for the rest of the codebase.

check: {
  each: {
    statements: 70,
    overrides: {
      'src/legacy/old-widget.js': { statements: 20 }
    }
  }
}

This keeps the exception explicit and visible in the config, rather than quietly lowering the global threshold for everyone.

Common Mistakes

  • Setting an aspirational threshold far above current coverage, immediately failing every CI run.
  • Lowering the global threshold to accommodate one poorly-covered legacy file instead of using a targeted override.
  • Never revisiting thresholds after initially setting them, missing the chance to raise the bar as coverage genuinely improves.
  • Treating a passing threshold check as proof of adequate testing rather than a minimum floor.

Key Takeaways

  • coverageReporter.check enforces minimum coverage thresholds, failing CI when they're not met.
  • global sets one project-wide threshold; each sets a per-file minimum, with overrides for exceptions.
  • Initial thresholds should be set close to current real coverage, then raised deliberately over time.
  • Thresholds are a regression-prevention floor, not a substitute for meaningful test quality.

Pro Tip

Revisit coverage thresholds on a schedule (quarterly is a reasonable cadence for many teams) rather than never touching them again after the initial setup. A threshold that was reasonable a year ago is often quietly out of date with where the codebase's actual coverage has since drifted.