Stage 5 · Platform
Testing & Quality Gates
Coverage Gates
Publishing Cobertura, LCOV, and JaCoCo reports while enforcing changed-line coverage thresholds.
What Coverage Measures
Code coverage measures what percentage of your code is executed by tests. It tells you what is tested, not how well it is tested. 100% coverage with shallow assertions is worse than 80% coverage with thorough edge-case testing.
Coverage is a useful signal in CI — it catches untested code before it reaches production. But it should be treated as a minimum bar, not a goal. Aim for high coverage on critical paths, not across the entire codebase.
Coverage Formats
| Format | Tool | Use Case |
|---|---|---|
| Cobertura XML | Istanbul, JaCoCo | GitLab MR coverage widget |
| LCOV | Istanbul, lcov | GitHub coverage reports |
| JaCoCo XML | JaCoCo | Java projects, Jenkins |
| HTML | Istanbul, coverage.py | Detailed per-file reports |
GitHub Actions Coverage
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
cache: npm
- run: npm ci
- name: Run tests with coverage
run: npm test -- --coverage --coverageReporters=text --coverageReporters=cobertura
- name: Coverage threshold check
run: |
COVERAGE=$(node -e "
const report = require('./coverage/cobertura-coverage.json');
console.log(report.line-rate * 100);
")
echo "Coverage: $COVERAGE%"
if (( $(echo "$COVERAGE < 80" | bc -l) )); then
echo "::error::Coverage $COVERAGE% is below 80% threshold"
exit 1
fi
- name: Comment coverage on PR
uses: orgoro/coverage@v3.2
with:
coverageFile: coverage/cobertura-coverage.json
token: ${{ secrets.GITHUB_TOKEN }}
thresholdAll: 0.80
thresholdNew: 0.90The coverage threshold check parses the Cobertura JSON and compares the line rate. The coverage action comments on the PR showing coverage for changed files. thresholdNew requires 90% coverage on new code.
GitLab CI Coverage
test:
stage: test
script:
- npm test -- --coverage
coverage: '/Statements\s*:\s*(\d+\.\d+)%/'
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage/cobertura.xml
paths:
- coverage/The coverage keyword uses a regex to extract the coverage percentage from the test output. GitLab displays this percentage in the merge request widget. The Cobertura report enables file-level coverage diffing in the MR.
Changed-Line Coverage
Changed-line coverage measures what percentage of your new or modified code is covered by tests. This is more actionable than total coverage — it tells you whether the code you are adding is tested, regardless of legacy code coverage.
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- run: npm ci
- name: Generate coverage report
run: npm test -- --coverage
- name: Check changed-line coverage
run: |
npx diff-cover coverage/cobertura-coverage.xml --compare-branch=origin/main --fail-under=80
- name: Coverage comment on PR
if: github.event_name == 'pull_request'
run: |
npx diff-cover coverage/cobertura-coverage.xml --compare-branch=origin/main --markdown-summary=diff-coverage.md
- name: Post comment
if: github.event_name == 'pull_request'
uses: marocchino/sticky-pull-request-comment@v2
with:
path: diff-coverage.mddiff-cover compares coverage against origin/main to identify which lines were changed and whether they are covered. --fail-under=80 fails the pipeline if changed-line coverage drops below 80%. The markdown summary is posted as a sticky comment on the PR.
Setting Thresholds
- Start with what you have — measure current coverage, set the threshold slightly below it, and ratchet up over time.
- Use different thresholds for total vs changed-line — changed-line should be higher (85-95%) than total (70-80%).
- Do not chase 100% — focus coverage on critical paths, business logic, and security-sensitive code.
- Enforce on PRs, not just main — catch coverage drops before they merge, not after.
High coverage with weak assertions gives a false sense of security. A test that runs code without asserting behavior counts toward coverage but catches no bugs. Combine coverage gates with mutation testing or test quality metrics for better signal.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.