On this page
Test coverage
Code coverage tells you which lines, branches, and functions of your code
actually ran while your tests executed. Deno has coverage built in: no
instrumentation step, no extra dependencies. The workflow is two commands:
deno test --coverage collects the data, and
deno coverage turns it into a report.
Collect coverage data Jump to heading
Pass --coverage when you run your tests:
deno test --coverage
This writes raw coverage data into a coverage/ directory as one JSON profile
per module. The data comes directly from the V8 JavaScript engine, which tracks
execution as your code runs, so the numbers reflect exactly what executed rather
than an approximation from source rewriting.
Deno also prints a coverage summary table after the test results and writes an
lcov.info file and an HTML report into the coverage directory. If you only
want the raw profiles, pass --coverage-raw-data-only.
To use a different directory, give the flag a value, or set the
DENO_COVERAGE_DIR environment variable:
deno test --coverage=cov_profile
Coverage data accumulates across runs. When you rename or delete files, stale
profiles can linger and skew the report, so pass --clean to empty the
directory before the tests run:
deno test --clean --coverage
Collect coverage outside deno test Jump to heading
deno run accepts the same --coverage flag, which is useful when the code you
want to measure doesn't run under the test runner: integration scripts, CLI
invocations of your tool, or a server exercised from the outside.
deno run --coverage=cov_profile main.ts
When your tests spawn Deno subprocesses, set the DENO_COVERAGE_DIR environment
variable instead. Child processes inherit it, so their execution is collected
into the same directory and shows up in the combined report:
DENO_COVERAGE_DIR=cov_profile deno test --allow-run
deno coverage cov_profile
Read the report Jump to heading
Point deno coverage at the directory you collected into:
deno coverage coverage/
For a fresh project created with deno init, which generates a main.ts HTTP
handler and a main_test.ts that exercises it, the report looks like this:
$ deno coverage coverage/
| File | Branch % | Function % | Line % |
| --------- | -------- | ---------- | ------ |
| main.ts | 75.0 | 100.0 | 80.0 |
| All files | 75.0 | 100.0 | 80.0 |
Each row shows three percentages for a file:
- Branch %: how many conditional paths (each side of an
if, ternary, and so on) were taken - Function %: how many declared functions were called at least once
- Line %: how many executable lines ran
To see which lines were missed, add --detailed. Uncovered lines are listed
under each file:
$ deno coverage --detailed coverage/
cover file:///dev/my-project/main.ts ... 80.000% (12/15)
16 | if (import.meta.main) {
17 | Deno.serve(handler);
18 | }
Here the tests import and call the handler function directly but never start
the server, so the import.meta.main block at the bottom of main.ts is the
code that never ran. That is exactly the kind of insight to act on: either test
the missing path or exclude it deliberately.
Generate an HTML report Jump to heading
For anything bigger than a couple of files, the HTML report is easier to navigate. It shows the summary table as a clickable file tree, with each source file rendered line by line and uncovered code highlighted:
deno coverage --html coverage/
This writes the report to coverage/html/. Open coverage/html/index.html in a
browser to explore it.
Export lcov for CI Jump to heading
Most coverage services and editor extensions consume the
lcov format. Export one with
--lcov:
deno coverage --lcov --output=coverage.lcov coverage/
Without --output, the lcov report is written to stdout. In a GitHub Actions
workflow, generate the file and upload it to your coverage service, for example
with the Codecov action:
steps:
- uses: actions/checkout@v4
- uses: denoland/setup-deno@v2
- run: deno test --coverage
- run: deno coverage --lcov --output=coverage.lcov coverage/
- uses: codecov/codecov-action@v5
with:
files: coverage.lcov
Enforce a coverage threshold Jump to heading
deno coverage reports coverage but has no built-in flag to fail when coverage
drops below a target. There are two common ways to gate on coverage:
- Let your coverage service do it. Codecov, Coveralls, and similar services can fail a pull request status check when coverage decreases or falls below a configured target, and they track trends over time.
- Check the lcov file yourself in CI. Each file's record contains
LF(lines found) andLH(lines hit), so summing them gives the overall line coverage:
deno coverage --lcov coverage/ > coverage.lcov
awk -F: '/^LF/ {lf += $2} /^LH/ {lh += $2}
END {pct = 100 * lh / lf; printf "line coverage: %.1f%%\n", pct;
exit (pct < 80)}' coverage.lcov
The awk script exits non-zero when line coverage is below 80%, which fails the
CI step.
Choose which files appear Jump to heading
By default the report includes your local code and its imports (URLs matching
^file:), and excludes anything with test.js, test.mjs, test.ts,
test.jsx, or test.tsx in its name so your test files do not inflate the
numbers. Adjust this with the --include and --exclude regex options. A file
appears in the report only if it matches the include pattern and does not match
the exclude pattern:
# Report on main.ts only
deno coverage --include="main.ts" coverage/
# Also include remote modules fetched over https
deno coverage --include="^file:|https:" coverage/
Ignore code in the report Jump to heading
Some code is intentionally untested: platform-specific fallbacks, debug helpers,
or an entry-point block like the import.meta.main example above. Mark it with
coverage ignore comments and it is treated as blank lines rather than counted
against you:
// deno-coverage-ignore
console.log("this single line is ignored");
// deno-coverage-ignore-start
if (import.meta.main) {
Deno.serve(handler);
}
// deno-coverage-ignore-stop
To drop a whole file from the report, put // deno-coverage-ignore-file at the
top of the file. Every -start comment needs a matching -stop, and ranges
cannot be nested. See
ignoring code in the reference
for the full rules.
Keep going Jump to heading
- Testing in Deno: the test runner, assertions, and mocking
deno coveragereference: every flag and ignore-comment ruledeno testreference: all test runner options, including coverage collection