name: test-coverage description: Run unit tests and measure test coverage for backend/frontend projects, identify coverage gaps, and improve tests based on project requirements. Use when the user asks to run tests, check coverage, increase coverage, or add missing unit tests.
Test Coverage Skill
Run tests and measure coverage in a project-agnostic way, preferring existing project commands and conventions.
This skill aligns with project-bootstrap conventions (common layout: core/, portal/, scripts/, docker/, docs/qa/, docs/ticket/), but should work with other layouts by discovering commands.
Workflow
- Discover the test commands already defined by the project.
- Run unit tests first (fast feedback).
- If coverage is requested: run coverage tooling and identify high-value gaps.
- Improve tests focusing on behavior and critical branches, not raw percentages.
- Re-run tests and coverage to confirm improvement.
Discover Existing Commands
From repo root, look for:
- Backend:
- Rust:
core/Cargo.toml - Makefile targets:
Makefile,core/Makefile
- Rust:
- Frontend:
portal/package.json
Prefer running existing scripts over inventing new ones.
Backend (Rust, if core/ exists)
Unit Tests
cd core
cargo test
Coverage
Prefer cargo llvm-cov if the project uses it (or has Makefile wrappers). Typical options:
cd core
cargo llvm-cov
cargo llvm-cov --html
Why llvm-cov over tarpaulin? tarpaulin has known issues with
#[async_trait]macros (reports dramatically lower coverage for async trait code).cargo-llvm-covuses LLVM's instrumentation and correctly tracks async trait coverage.
If a Makefile defines coverage targets, use those instead:
cd core
make coverage
make coverage-html
Notes:
- Coverage exclusions (migrations, generated code, thin wiring) are project-specific. Follow the project's existing exclusions if present.
- Document coverage exclusions so reviewers understand why files are skipped:
| Example Exclusion | Typical Reason |
|---|---|
repository/*.rs |
Thin data mapping layer (ORM-equivalent), no business logic |
main.rs |
Program entry point |
migration/*.rs |
Database migration scripts |
src/index.ts |
Barrel re-export, no logic |
src/types/**/*.ts |
Pure type definitions, no runtime code |
- Treat coverage as a signal: prioritize service/business logic and error branches.
Frontend (TypeScript, if portal/ exists)
Run the project's test command (examples):
cd portal
npm test
If Vitest is used:
cd portal
npx vitest --run
Coverage (examples):
cd portal
npm test -- --coverage
Coverage-Driven Improvement Guidance
When asked to "complete tests" or "increase coverage":
- Add tests for behaviors users rely on (API validations, authz checks, invariants, boundary conditions).
- Prefer testing pure business logic without Docker.
- Avoid brittle implementation-detail assertions.
- For E2E gaps, add only a few stable tests for critical flows (see
e2e-testingskill).
Coverage Targets (Guidance)
All test coverage targets follow a project minimum of >=90% where possible:
| Layer | Target | Notes |
|---|---|---|
| Domain/Business logic | >=90% | Pure validation, no I/O |
| Service layer | >=90% | Core business logic |
| API handlers | >=90% | HTTP routing and request validation |
| gRPC handlers (if applicable) | >=90% | RPC service methods |
| Repository layer | N/A | Often excluded from tracking (thin data mapping) |
| Frontend utils/services | >=90% | Non-UI logic |
HTTP Handler Testing with Dependency Injection
If the project uses a DI / HasServices trait pattern (generic handlers with swappable state), test production handler code by providing a test state that implements the same trait:
Production: AppState (impl HasServices) → build_router() → Handlers<AppState>
Tests: TestAppState (impl HasServices) → build_router() → Handlers<TestAppState>
↑
Same production handlers!
Key rules:
- Always use the trait for new handlers — never use concrete
AppStatedirectly. - Test production code —
build_test_router()should use productionbuild_router(state). - Mock external services with wiremock or equivalent.
- Use test repositories — in-memory implementations of repository traits.
Troubleshooting
- Compilation errors: Run
cargo cleanfirst - Mock expectations not met: Check predicate conditions
- Low coverage with tarpaulin: Use
cargo llvm-covinstead (tarpaulin can't track#[async_trait]macros properly) - llvm-cov not found: Install with
cargo install cargo-llvm-cov