test-coverage
SKILL.md
Test Coverage Analysis Skill
When analyzing test coverage, follow this structured process. The goal is not 100% coverage — it's ensuring the riskiest code is tested well.
1. Discover the Testing Setup
Before analyzing, understand the project's testing landscape:
# Detect testing framework
# Node.js
cat package.json | grep -E "jest|vitest|mocha|ava|tap|playwright|cypress|testing-library"
# Python
cat requirements.txt pyproject.toml setup.cfg 2>/dev/null | grep -E "pytest|unittest|nose|coverage|tox"
# Ruby
cat Gemfile 2>/dev/null | grep -E "rspec|minitest|capybara|factory_bot"
# Go
grep -r "_test.go" --include="*.go" -l .
# Java
cat pom.xml build.gradle 2>/dev/null | grep -E "junit|mockito|testng|jacoco"
# PHP
cat composer.json 2>/dev/null | grep -E "phpunit|pest|mockery"
Identify:
- Testing framework in use (Jest, Vitest, Pytest, RSpec, JUnit, etc.)
- Coverage tool configured (Istanbul/nyc, coverage.py, SimpleCov, JaCoCo, etc.)
- Test directory structure (co-located vs separate test folder)
- Naming conventions (*.test.ts, .spec.ts, test_.py, *_test.go)
- Test types present (unit, integration, e2e, snapshot)
- CI integration (are tests running in CI? is coverage enforced?)
2. Run Existing Coverage
# Node.js (Jest)
npx jest --coverage --coverageReporters=text
# Node.js (Vitest)
npx vitest run --coverage
# Python (Pytest)
python -m pytest --cov=. --cov-report=term-missing
# Go
go test -coverprofile=coverage.out ./...
go tool cover -func=coverage.out
# Ruby (RSpec)
COVERAGE=true bundle exec rspec
# Java (Maven + JaCoCo)
mvn test jacoco:report
# PHP (PHPUnit)
php artisan test --coverage
Record:
- Overall line coverage percentage
- Overall branch coverage percentage
- Files with 0% coverage (completely untested)
- Files with < 50% coverage (poorly tested)
- Uncovered lines (specific line numbers)
3. Identify What's NOT Tested
3a. Find Files Without Tests
# Node.js — find source files without matching test files
find src -name "*.ts" -o -name "*.js" | while read f; do
base=$(basename "$f" | sed 's/\.\(ts\|js\)$//')
if ! find . -name "${base}.test.*" -o -name "${base}.spec.*" | grep -q .; then
echo "NO TEST: $f"
fi
done
# Python — find modules without test files
find src -name "*.py" ! -name "__init__.py" | while read f; do
base=$(basename "$f" .py)
if ! find . -name "test_${base}.py" -o -name "${base}_test.py" | grep -q .; then
echo "NO TEST: $f"
fi
done
# Go — find packages without test files
find . -name "*.go" ! -name "*_test.go" -exec dirname {} \; | sort -u | while read d; do
if ! ls "$d"/*_test.go 2>/dev/null | grep -q .; then
echo "NO TEST: $d"
fi
done
3b. Find Untested Code Paths
Look for these commonly missed patterns:
- Error handlers and catch blocks — the most commonly untested code
- Edge cases — null, undefined, empty arrays, zero, negative numbers, boundary values
- Else branches — the unhappy path in if/else
- Switch default cases — fallback handling
- Early returns and guard clauses — validation at the top of functions
- Timeout and retry logic — what happens when things fail
- Race conditions — concurrent operations
- Cleanup code — finally blocks, destructors, shutdown handlers
- Configuration branches — code that runs differently per environment
- Deprecated or feature-flagged code — code behind flags that's still reachable
3c. Find Dead or Unreachable Code
# Node.js — find unused exports
npx ts-prune
# Python — find unused code
pip install vulture && vulture src/
# General — find functions not referenced anywhere
grep -rn "function\|def\|func " src/ | while read line; do
fname=$(echo "$line" | grep -oP '(?:function|def|func)\s+\K\w+')
count=$(grep -rn "$fname" src/ | wc -l)
if [ "$count" -le 1 ]; then
echo "POSSIBLY UNUSED: $line"
fi
done
4. Risk-Based Prioritization
Not all untested code is equally important. Prioritize by risk:
🔴 Critical — Test These First
- Authentication and authorization — login, signup, password reset, permission checks
- Payment and billing — charge, refund, subscription logic
- Data mutation — create, update, delete operations
- API endpoints — especially public-facing ones
- Input validation — sanitization and parsing of user input
- Security-sensitive code — encryption, token generation, access control
- Core business logic — the main value of your application
🟠 High — Test These Next
- Error handling — catch blocks, error boundaries, fallback behavior
- Database queries — complex queries, transactions, migrations
- Third-party integrations — API calls, webhooks, callbacks
- State management — reducers, stores, state transitions
- File operations — uploads, downloads, processing
- Background jobs — queues, cron jobs, workers
🟡 Medium — Test When Possible
- UI components — interactive components, forms, modals
- Utility functions — helpers, formatters, transformers
- Configuration — environment-specific logic
- Middleware — request/response processing pipeline
- Caching logic — cache invalidation, TTL, fallbacks
🟢 Low — Test If Time Permits
- Static components — presentational components with no logic
- Type definitions — interfaces, types, enums
- Constants and config objects — static values
- Logging — log formatting and output
- Dev-only code — seeders, fixtures, debug utilities
5. Test Quality Analysis
Coverage percentage alone doesn't mean tests are good. Analyze quality:
Assertion Quality
// 🔴 BAD — test runs but asserts nothing meaningful
test('creates user', async () => {
const result = await createUser({ name: 'Alice' });
expect(result).toBeTruthy(); // too vague
});
// ✅ GOOD — specific, meaningful assertions
test('creates user with correct fields', async () => {
const result = await createUser({ name: 'Alice' });
expect(result.id).toBeDefined();
expect(result.name).toBe('Alice');
expect(result.createdAt).toBeInstanceOf(Date);
});
Test Independence
// 🔴 BAD — tests depend on each other's state
let userId;
test('creates user', async () => {
const user = await createUser({ name: 'Alice' });
userId = user.id;
});
test('fetches user', async () => {
const user = await getUser(userId); // depends on previous test
});
// ✅ GOOD — each test sets up its own state
test('fetches user', async () => {
const created = await createUser({ name: 'Alice' });
const fetched = await getUser(created.id);
expect(fetched.name).toBe('Alice');
});
Common Test Smells
- No assertions — test runs code but checks nothing
- Testing implementation, not behavior — brittle tests that break on refactors
- Over-mocking — mocking so much that the test proves nothing
- Flaky tests — tests that pass/fail randomly (timing, order-dependent, network)
- Duplicate tests — same scenario tested multiple times in different places
- Giant test files — 1000+ line test files that are hard to maintain
- Missing cleanup — tests that leave behind state (DB records, files, env vars)
- Snapshot overuse — snapshots accepted without review, hiding regressions
- Copy-paste tests — duplicated setup that should be extracted into helpers
- Happy path only — only testing success, never failure
6. Stack-Specific Checks
Node.js / Jest / Vitest
- Check for missing
afterEachcleanup (open handles, DB connections) - Verify async tests use
awaitor return promises (silent failures otherwise) - Check for missing
jest.mock()cleanup between tests - Look for
setTimeoutin tests withoutjest.useFakeTimers() - Verify snapshot tests are intentional and reviewed
- Check for missing error boundary tests in React components
- Verify
act()wrapping on React state updates in tests - Look for missing
waitFor/findByon async UI updates
Python / Pytest
- Check for missing
conftest.pyfixtures for common setup - Verify database tests use transactions and rollback (
@pytest.mark.django_db) - Look for missing
parametrizeon tests that should cover multiple inputs - Check for missing
mock.patchcleanup (use context managers or decorators) - Verify async tests use
@pytest.mark.asyncio - Check for missing exception tests (
with pytest.raises(...)) - Look for hardcoded file paths in tests (use
tmp_pathfixture) - Verify test isolation — no tests reading/writing shared state
React / Next.js
- Check for missing
rendertests on all user-facing components - Verify form components test validation, submission, and error states
- Look for missing accessibility tests (
@testing-library/jest-dommatchers) - Check for untested loading and error states
- Verify hooks are tested with
renderHookfrom testing-library - Check for missing tests on context providers and consumers
- Look for untested route guards and redirects
- Verify Server Components have integration tests
- Check for missing tests on API routes / Server Actions
Vue / Nuxt
- Check for missing
mount/shallowMounttests on components - Verify Pinia stores are tested with
createTestingPinia - Look for missing
emitted()checks on event emissions - Check for untested computed properties and watchers
- Verify composables are tested independently
- Check for missing tests on Nuxt middleware and plugins
Go
- Check for missing table-driven tests on functions with multiple inputs
- Verify error return values are tested (not just happy path)
- Look for missing
t.Parallel()on independent tests - Check for missing
t.Helper()on test utility functions - Verify interfaces are tested with mock implementations
- Check for missing benchmark tests on performance-critical code (
func BenchmarkX) - Look for missing
t.Cleanup()for resource teardown - Verify HTTP handlers are tested with
httptest.NewServer
Java / Spring Boot
- Check for missing
@SpringBootTestintegration tests - Verify
@MockBeanis used appropriately (not over-mocked) - Look for missing
@Transactionalon database tests (auto rollback) - Check for missing controller tests with
MockMvc - Verify exception handlers are tested
- Check for missing
@ParameterizedTeston multi-input tests - Look for untested
@Scheduledtasks and async methods - Verify repository custom queries have integration tests
Ruby / RSpec
- Check for missing
describeblocks for each public method - Verify
letandbeforeblocks handle proper setup/teardown - Look for missing
contextblocks for different scenarios - Check for missing
shared_examplesfor common behavior - Verify factory definitions cover all required fields (
FactoryBot) - Check for missing request specs on API endpoints
- Look for untested ActiveRecord callbacks and validations
- Verify background jobs (Sidekiq/Resque) have specs
PHP / Laravel
- Check for missing Feature tests on routes and controllers
- Verify database tests use
RefreshDatabaseorDatabaseTransactions - Look for missing tests on form requests (validation rules)
- Check for missing
assertDatabaseHas/assertDatabaseMissingassertions - Verify mail, notification, and event fakes are used
- Check for missing tests on Eloquent scopes and accessors
- Look for untested middleware
- Verify queue jobs and listeners have tests
Mobile (React Native / Flutter)
- Check for missing widget/component tests
- Verify navigation flows are tested
- Look for missing tests on platform-specific code (iOS vs Android)
- Check for untested offline/error states
- Verify async storage operations are tested
- Check for missing tests on deep link handling
- Look for untested permission request flows
- Verify API response parsing is tested with realistic mock data
API / Integration Tests
- Check for missing tests on all HTTP methods (GET, POST, PUT, DELETE, PATCH)
- Verify authentication is tested (valid token, expired token, no token, wrong role)
- Look for missing tests on pagination, filtering, and sorting
- Check for missing tests on rate limiting behavior
- Verify webhook handlers are tested with realistic payloads
- Check for missing tests on file upload/download endpoints
- Look for untested CORS behavior
- Verify error responses match API documentation/contract
Database
- Check for missing migration tests (up and rollback)
- Verify complex queries are tested with realistic data volumes
- Look for missing tests on database constraints (unique, foreign key, check)
- Check for untested transaction behavior (commit, rollback, deadlock)
- Verify connection pooling and timeout handling is tested
- Check for missing seed data validation tests
7. Coverage Improvement Plan
After analysis, provide a prioritized plan:
Immediate (This Sprint)
- List specific files and functions to test first based on risk
- Provide test scaffolding for the highest-priority untested code
- Suggest specific test cases with descriptions
Short-Term (Next 2 Sprints)
- Increase coverage on medium-risk areas
- Add integration tests for critical user flows
- Fix test quality issues (weak assertions, flaky tests)
Long-Term (This Quarter)
- Set up coverage thresholds in CI (fail build if coverage drops)
- Add e2e tests for critical user journeys
- Implement mutation testing to verify test effectiveness
- Set up coverage trend tracking
Output Format
Coverage Report
Overall Coverage:
| Metric | Current | Target | Gap |
|---|---|---|---|
| Line Coverage | X% | 80% | X% |
| Branch Coverage | X% | 70% | X% |
| Function Coverage | X% | 85% | X% |
Untested Files (by risk):
🔴 Critical — No Tests:
src/auth/login.ts— handles user authenticationsrc/payments/charge.ts— processes payments
🟠 High — Partial Coverage:
src/api/users.ts— 40% covered, missing error pathssrc/db/queries.ts— 55% covered, missing edge cases
Test Quality Issues:
tests/user.test.ts:45— assertion too vague (toBeTruthy)tests/order.test.ts— tests are order-dependenttests/api.test.ts:120— over-mocked, not testing real behavior
For Each Untested Area
File: src/auth/login.ts
- Risk: 🔴 Critical
- Why it matters: Handles user authentication — bugs here = security breach
- Missing tests:
- Valid login with correct credentials
- Login with wrong password (should return 401)
- Login with non-existent email (should return 401, same message as wrong password)
- Login with expired account
- Rate limiting after 5 failed attempts
- SQL injection attempt in email field
- Session creation and token generation
- Test scaffold:
describe('login', () => {
it('returns a token for valid credentials', async () => { ... });
it('returns 401 for incorrect password', async () => { ... });
it('returns 401 for non-existent email', async () => { ... });
it('locks account after 5 failed attempts', async () => { ... });
it('rejects SQL injection in email', async () => { ... });
});
Summary
End every analysis with:
- Current state — Overall coverage and quality assessment
- Biggest gaps — The riskiest untested code
- Top 5 tests to write first — Highest impact, with brief descriptions
- Test quality issues — Problems with existing tests that need fixing
- CI recommendations — Coverage thresholds, pre-commit hooks, reporting
- What's done well — Good testing practices already in place to maintain
Weekly Installs
1
Repository
aakash-dhar/cla…e-skillsFirst Seen
7 days ago
Security Audits
Installed on
zencoder1
amp1
cline1
openclaw1
opencode1
cursor1