A coverage gate for any Cobertura report: one command tells you, your CI, or your coding agent whether coverage passes, and exactly which lines and branches to test next.
dotnet tool install -g DotCov.ToolRun the tests and gate them in one step (.NET):
dotcov test --min-line 90 --min-branch 80Or gate a report you already have (.NET, TypeScript, Python, anything that writes Cobertura):
dotcov check TestResults/ --min-line 90 --min-branch 80FAIL: line 81.8% (min 90%), branch 50.0% (min 80%) - line and branch coverage below threshold
files below line or branch threshold:
src/Orders.cs 77.7% lines 33.3% branches
uncovered 14-15 · branches 12 (1/2), 18 (1/4)
The first line is the verdict, and the exit code says the same: 0 pass, 1 fail. Below it,
every file under a threshold, worst first, with the lines no test runs and the branches only
partly taken. Add tests for those lines, run the command again, repeat until it passes.
Thresholds are required: pass --min-line, --min-branch, or both (0 to 100). A dimension you
leave out is not checked.
dotcov is built to be driven by a coding agent:
-
One verdict, one place. Stdout carries only data; the verdict (
PASS,FAIL,NODATA,DISABLED) is always the first line of stderr. The exit code alone is enough to branch on. -
JSON everywhere.
--format jsononreport,check,test,crapanddiff(andsnapshot, which is JSON) prints one document withschemaVersion,command,inputsandwarnings.check,testandcrapadd agateobject; itsoffenderslist is the work queue:dotcov check TestResults/ --min-line 90 --format json
"gate": { "outcome": "fail", "exitCode": 1, "reason": "FAIL: line 81.8% (min 90%), branch 50.0% (min 80%) - line and branch coverage below threshold", "offenders": [ { "path": "src/Orders.cs", "lineRate": 77.77, "branchRate": 33.33, "uncovered": "14-15", "partialBranches": [ { "line": 12, "covered": 1, "total": 2 } ] } ] }
-
Few tokens. Uncovered lines are ranges (
"14-15,21"),--top Ncaps the list (default 20) and says how many more there are, and--no-color(accepted by every command that reads a report) orNO_COLOR=1strips ANSI. -
Help where you are.
dotcov <command> --helpprints only that command. A mistyped flag answersDid you mean '--min-line'?and lists the command's valid options, with exit2. -
Nothing silent. An exclusion, a report that matched no file, a method that could not be scored: each is named on stderr and in the JSON, never dropped.
| Command | Does |
|---|---|
dotcov test [<project>] --min-line N |
Runs dotnet test with coverage into a fresh TestResults/<run>, then gates that run like check. Arguments after -- go to dotnet test |
dotcov check <path> --min-line N |
Gates line and branch coverage of a report file or directory |
dotcov report <path> |
Shows coverage per file; --top N shows the N worst files |
dotcov crap <path> |
Gates per-method risk, comp² · (1 − cov)³ + comp, worst first with file:line |
dotcov diff <before> <after> |
Shows the files whose coverage changed |
dotcov snapshot <path> |
Writes JSON stamped with --commit, --branch, --project |
A directory is searched for **/*cobertura*.xml and every match is merged, so a sharded test
matrix needs no merge step. Other file names need --pattern (--pattern "**/coverage.xml").
With Microsoft coverage, dotcov test also flushes pending counters when a managed child
terminates with an unhandled exception. Existing startup hooks, the exception and its
process exit are preserved.
dotcov --help lists every option.
| Code | Verdict | Meaning |
|---|---|---|
| 0 | PASS |
Thresholds met (report, diff, snapshot: the command succeeded) |
| 1 | FAIL |
Below a threshold |
| 2 | error: |
Usage: unknown command or option, invalid value, no threshold |
| 3 | NODATA |
Nothing to judge: no lines measured, an unscored method, an unresolved file |
| 4 | DISABLED |
Every threshold is 0, so the gate cannot fail |
| 5 | error: |
A report or file could not be read, written or uploaded |
| 6 | error: |
dotcov test: the test run failed; coverage was not evaluated |
Source lines count once per file and line. Branches from distinct packages, classes and methods count separately, even when they share a source line and condition numbers. Repeated method data and class summaries are deduplicated across reports.
Library consumers can inspect branch ownership through FileCoverage.BranchesByOrigin,
keyed by package, class, method and signature. BranchesByLine contains the combined
counts. Conflicting condition identities within one owner produce a warning.
dotcov crap TestResults/ --max-crap 30Method Comp Cov % CRAP Location
------------------------------------------------------
App.Orders.Place 6 66.7% 7.4 src/Orders.cs:12
App.Orders.Cancel 3 100.0% 3.0 src/Orders.cs:30
A fully covered method scores its complexity, so a high score on covered code means split it,
not test it. crap is for .NET: Coverlet embeds complexity in its report, and for other .NET
emitters --metrics <file> from dotnet msbuild /t:Metrics supplies it. Istanbul (Vitest, c8,
Jest) and coverage.py write none, so crap answers NODATA for their reports; gate them with
check. Every method must be scored: one the gate cannot score makes it NODATA, never a pass.
DotCov reads any Cobertura report. TypeScript with Vitest (@vitest/coverage-v8):
npx vitest run --coverage.enabled --coverage.reporter=cobertura --coverage.include='src/**'
dotcov check coverage/cobertura-coverage.xml --min-line 90 --min-branch 90With c8, around node --test or any other runner, no config needed:
npx c8 --all --src src --reporter=cobertura node --test
dotcov check coverage/cobertura-coverage.xml --min-line 90 --min-branch 90Both report only the files a test loaded unless told where the sources are:
--coverage.include and --all --src keep a module no test imports in the report, at 0%.
Vitest maps V8 coverage back to the TypeScript source, so every branch counts; c8 counts the
blocks V8 ran and leaves out the branches of functions no test called, so its branch rate can
read higher. Jest works too:
npm test -- --coverage --coverageProvider=v8 --coverageReporters=cobertura.
Python with coverage.py:
python -m coverage run --branch --source=your_package -m pytest
python -m coverage xml -o coverage.xml
dotcov check coverage.xml --min-line 90 --min-branch 90When a report declares several <source> roots, a relative file name does not say which root
it belongs to. DotCov never guesses: the gate answers NODATA until you add --probe-sources
(resolve against the disk, where the sources are checked out) or --source-root <root>. CI
checks DotCov's counts against the producers' own numbers on real upstream projects; see the
consumer test notes.
--exclude-generated skips generated code, build output, migrations, GlobalUsings.cs and
Program.cs. --exclude-test-code skips test projects and fixtures. --exclude-external skips
code built elsewhere: dependencies your coverage tool instrumented, reported under SourceLink
paths (/_/src/...); without it a warning names them, because they count toward every rate.
--keep <substrings> exempts paths from all three. Every command that reads a report takes
them (diff applies them to both sides). Stderr and the JSON (excluded, exclusionRules)
list what was left out.
check and test draw their verdict with --badge-dir <dir>. The badge action gates and
publishes it to a badges branch in one step:
permissions:
contents: write
steps:
# … checkout, setup-dotnet, and a test run writing Cobertura to TestResults/
- uses: ANcpLua/dotcov/.github/actions/badge@v4.7.0
with:
reports: TestResults
min-line: 80
min-branch: 60
publish: ${{ github.ref == 'refs/heads/main' }}Green is PASS, red FAIL, grey NODATA, yellow DISABLED. If the number is far below what
you expect and dotcov warns about SourceLink paths (/_/...), your coverage tool measured your
dependencies too: limit it to your assemblies (coverlet: --coverlet-include "[YourAssembly]*")
or pass args: --exclude-external. The other inputs are in
action.yml. A private repository cannot serve the image
to GitHub's image proxy.
| Package | For |
|---|---|
| DotCov.Tool | The dotcov command; Native AOT on macOS ARM64 and Linux, portable elsewhere |
| DotCov | The parser and gates as a library, no dependencies |
| DotCov.Fallout | One interface that adds coverage targets to a Fallout build |
dotnet tool install -g Fallout.GlobalTool # once
fallout Test # the tests
fallout Coverage # the tests plus the 100% coverage gateIssues · Release notes · MIT