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").
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 |
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.3.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