Skip to main content
Bun has built-in code coverage reporting. Pass --coverage to see which lines and functions are exercised by your tests.

Enabling coverage

bun test --coverage
Bun prints a coverage summary to the console after the test run:
-------------|---------|---------|-------------------
File         | % Funcs | % Lines | Uncovered Line #s
-------------|---------|---------|-------------------
All files    |   85.71 |   90.48 |
 src/         |   85.71 |   90.48 |
  utils.ts   |  100.00 |  100.00 |
  api.ts     |   75.00 |   85.71 | 15-18,25
  main.ts    |   80.00 |   88.89 | 42,50-52
-------------|---------|---------|-------------------
  • % Funcs — percentage of functions called during tests
  • % Lines — percentage of executable lines that were run
  • Uncovered Line #s — specific lines that were not executed

Always-on coverage

Enable coverage by default in bunfig.toml:
bunfig.toml
[test]
coverage = true

Coverage thresholds

Fail the test run if coverage falls below a specified level. This is useful in CI to enforce minimum quality gates.

Simple threshold

Apply the same threshold to lines, functions, and statements:
bunfig.toml
[test]
coverageThreshold = 0.9   # 90% across all metrics

Per-metric thresholds

Set different thresholds for each coverage metric:
bunfig.toml
[test]
coverageThreshold = { lines = 0.85, functions = 0.90, statements = 0.80 }
When any threshold is configured, bun test exits with a non-zero code if coverage is below the threshold.

Coverage reporters

By default, coverage is printed as a text table. Use coverageReporter to add additional output formats:
bunfig.toml
[test]
coverageReporter = ["text", "lcov"]
coverageDir = "./coverage"   # default: "coverage"
ReporterDescription
textPrints a summary table to the console (default)
lcovWrites an lcov.info file to the coverage directory
Via CLI:
bun test --coverage --coverage-reporter=lcov

LCOV integration

The LCOV format is supported by a wide range of tools:

VS Code

Extensions like Coverage Gutters can display coverage inline in the editor using the lcov.info file.

Codecov / Coveralls

Upload coverage/lcov.info as a coverage artifact to track coverage trends over time.

GitHub Actions

Use the codecov/codecov-action to upload and comment on PRs automatically.

GitLab CI

Configure coverage_report artifacts to display coverage in merge requests.

Excluding files from coverage

Skip test files

By default, test files themselves are included in the coverage report. Exclude them with:
bunfig.toml
[test]
coverageSkipTestFiles = true

Path ignore patterns

Exclude specific files or directories using glob patterns:
bunfig.toml
[test]
coveragePathIgnorePatterns = [
  "**/*.spec.ts",
  "**/*.test.ts",
  "src/generated/**",
  "dist/**",
  "vendor/**",
  "*.config.ts"
]
Files matching any pattern are excluded from both the text summary and any generated report files.

CI/CD integration

GitHub Actions

.github/workflows/test.yml
name: Test with Coverage
on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: oven-sh/setup-bun@v2
      - run: bun install
      - run: bun test --coverage --coverage-reporter=lcov
      - name: Upload coverage to Codecov
        uses: codecov/codecov-action@v3
        with:
          file: ./coverage/lcov.info
          fail_ci_if_error: true

GitLab CI

.gitlab-ci.yml
test:coverage:
  stage: test
  script:
    - bun install
    - bun test --coverage --coverage-reporter=lcov
  coverage: '/Lines\s*:\s*(\d+.\d+)%/'
  artifacts:
    reports:
      coverage_report:
        coverage_format: cobertura
        path: coverage/lcov.info

Running coverage on a subset of tests

Run coverage for specific files or patterns:
# Coverage for a specific test file
bun test --coverage src/api.test.ts

# Coverage for tests matching a name pattern
bun test --coverage --test-name-pattern="API"

Sourcemaps

Bun transpiles all files internally and automatically applies sourcemaps so that coverage reports reference your original source lines. To disable this (rarely needed):
bunfig.toml
[test]
coverageIgnoreSourcemaps = true
When disabling sourcemaps, add // @bun at the top of source files to opt out of transpilation, otherwise line numbers will be confusing.

Coverage defaults

By default, coverage reports:
  • Exclude node_modules directories
  • Exclude files loaded through non-JS/TS loaders (e.g., .css, .txt)
  • Include test files (disable with coverageSkipTestFiles = true)

Best practices

High coverage does not guarantee good tests. Write assertions that verify behavior, not just lines that execute without throwing.
// Good: validates actual behavior
test("calculates tax correctly", () => {
  expect(calculateTax(100, 0.08)).toBe(8);
  expect(calculateTax(0, 0.08)).toBe(0);
});

// Avoid: executes lines without asserting anything
test("calculateTax runs", () => {
  calculateTax(100, 0.08); // no expect!
});
test("validates email format", () => {
  expect(validateEmail("[email protected]")).toBe(true);
  expect(validateEmail("")).toBe(false);          // empty string
  expect(validateEmail("nodomain")).toBe(false);   // missing @
  expect(validateEmail(null)).toBe(false);         // null input
});
Run bun test --coverage periodically to identify untested branches. Use the uncovered line numbers to guide where to add tests next.
Coverage instrumentation adds overhead. For fast feedback during development, run tests without --coverage and let CI enforce thresholds.

Build docs developers (and LLMs) love