Claude
Skills
Sign in
Back

test-coverage

Included with Lifetime
$97 forever

Analyzes test coverage gaps, identifies untested code paths, prioritizes which tests to write based on risk, and helps improve overall test quality. Use when the user says "what's not tested?", "check test coverage", "what tests are missing?", "improve coverage", or "are my tests good enough?".

Code Review

What this skill does


# 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:
```bash
# 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
```bash
# 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
```bash
# 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
```bash
# 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 `afterEach` cleanup (open handles, DB connections)
- Verify async tests use `await` or return promises (silent failures otherwise)
- Check for missing `jest.mock()` cleanup between tests
- Look for `setTimeout` in tests without `jest.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` / `f

Related in Code Review