Test Coverage Pipeline
What you'll learn
- Setting up per-package coverage thresholds
- Generating and merging lcov reports across a monorepo
- Configuring file and pattern exclusions
- Integrating coverage checks into CI with pass/fail gates
Prerequisites
- An existing Archipelago monorepo (see Monorepo Scaffolding)
- The coverage command generated in your monorepo toolkit
lcovinstalled (brew install lcovon macOS)
Step 1: Generate the Coverage Command
bash
archipelago generate coverageYou will be prompted for:
- appName — Your app name (e.g.,
MyApp) - defaultThreshold — Minimum coverage percentage (e.g.,
80)
Step 2: Understand the Generated Structure
The brick adds the coverage command to your existing monorepo toolkit:
devtools/
└── lib/src/
└── commands/
└── coverage/
├── coverage_command.dart # CLI entry point
├── lcov_parser.dart # Parse lcov.info files
├── coverage_merger.dart # Merge per-package reports
└── threshold_checker.dart # Enforce minimumsStep 3: Configure Coverage Thresholds
Create coverage_config.yaml in your monorepo root:
yaml
default_threshold: 80
package_thresholds:
packages/core_network: 90
packages/feature_auth: 85
packages/core_common: 95
exclude_from_coverage:
- "**/*.g.dart"
- "**/*.freezed.dart"
- "**/generated/**"
- "**/*.config.dart"
- "**/di/**"- default_threshold — Applied to packages without a specific override
- package_thresholds — Per-package minimum coverage percentages
- exclude_from_coverage — Glob patterns for files to exclude from reports
Step 4: Run Coverage Locally
Generate coverage reports for all packages or specific ones:
bash
# Run coverage for all packages
dart run devtools coverage
# Run coverage for a specific package
dart run devtools coverage --package packages/core_network
# Run coverage only for affected packages
dart run devtools coverage --affected --base mainExample output:
Coverage Report:
packages/core_common 96.2% (threshold: 95%) ✓
packages/core_network 87.4% (threshold: 90%) ✗ FAIL
packages/feature_auth 88.1% (threshold: 85%) ✓
app 72.3% (threshold: 80%) ✗ FAIL
2 packages below threshold. Exit code: 1Step 5: Merge lcov Reports
For a unified coverage report across the monorepo, merge individual lcov.info files:
bash
# Generate merged report
dart run devtools coverage --merge --output coverage/merged_lcov.info
# Generate HTML report from merged lcov
genhtml coverage/merged_lcov.info -o coverage/html
# Open the report
open coverage/html/index.htmlStep 6: Integrate with CI
Add coverage checks to your PR validation workflow:
yaml
- name: Run tests with coverage
run: dart run devtools coverage --affected --base origin/${{ github.base_ref }}
- name: Upload coverage report
if: always()
uses: actions/upload-artifact@v4
with:
name: coverage-report
path: coverage/merged_lcov.info
- name: Comment coverage on PR
if: github.event_name == 'pull_request'
run: |
report=$(dart run devtools coverage --affected --base origin/${{ github.base_ref }} --format markdown)
gh pr comment ${{ github.event.number }} --body "$report"Key Customization Points
| Customization | Where to Change |
|---|---|
| Default coverage threshold | coverage_config.yaml — default_threshold |
| Per-package thresholds | coverage_config.yaml — package_thresholds |
| Excluded file patterns | coverage_config.yaml — exclude_from_coverage |
| Output format | --format flag (text, json, markdown) |
| Merge reports | --merge flag with --output path |
Next Steps
- Set up affected detection to run coverage only on changed packages
- Visualize dependencies to understand your monorepo structure