coverage-analysis

verified

075f4124-f079-4474-9571-2cb002228583

Use when assessing test coverage — generate reports with pytest-cov, read missing-line output, and prioritize gaps by risk (error paths, auth, money) over raw percentages.

Metadata

Skill ID
075f4124-f079-4474-9571-2cb002228583
Version
1
Owner
387274b7-2891-478b-81b8-e11d5adb9319
Tags
testingcoveragequalitymetrics
Signature
verified
Integrity
OK
Content hash
0953d5432df7c84c69aba7853d070c40c330bcd61cdce86c82280103f24adcd6
Created
2026-08-15T05:24:22Z

Skill file

Raw skill file (markdown source)
# Coverage Analysis

**Use when** you need to assess test coverage, identify untested code paths, and prioritize which gaps to close. Coverage is a tool for finding *missing* tests, not a score to maximize.

## Generating a Coverage Report

```bash
# Basic coverage report with missing lines
pytest --cov=src/ --cov-report=term-missing -x

# Output per file:
# src/orders/processor.py    45     12    73%   34-38, 52-55, 67-70
#                           lines  missed  %    missing line ranges
```

### Generating HTML for Visual Drill-Down

```bash
pytest --cov=src/ --cov-report=html
# Open htmlcov/index.html in a browser
# Click any file to see green (covered) / red (uncovered) lines
```

### Strict Mode (CI)

```bash
# Fail if coverage drops below threshold
pytest --cov=src/ --cov-fail-under=80 --cov-report=term
```

## Reading the Missing-Line Report

```text
src/orders/processor.py    120    35    70%   14-18, 42, 55-60, 88-102
```

Interpretation:
- **14-18**: Early return / validation — HIGH priority (error handling)
- **42**: Single uncovered `if` branch — MEDIUM (probably a real code path)
- **55-60**: Exception handler — HIGH priority (error paths)
- **88-102**: A 15-line uncovered block — Check if dead code or missing feature test

## Prioritizing Gaps: Risk Over Percentage

Coverage percentage is a lagging indicator. Prioritize by *risk*:

### High-Priority Gaps (fix these first)

```python
# Error handlers
try:
    process_payment(...)
except PaymentError:
    refund(...)         # <-- UNCOVERED (high risk)
    log_failure(...)    # <-- UNCOVERED (high risk)

# Auth / permission checks
if not user.is_admin:
    raise Forbidden()   # <-- UNCOVERED (security)

# Money / billing
if amount > limit:
    require_approval()  # <-- UNCOVERED (financial)
```

### Low-Priority Gaps (can wait)

```python
# Logging statements
logger.debug("Processing batch %d", batch_id)  # <-- UNCOVERED (low risk)

# Cosmetic formatting
def format_currency(amount):
    if currency == "JPY":                       # <-- UNCOVERED (JPY support not built)
        return f"¥{int(amount):,}"
```

### Prioritization Table

| Gap Type | Priority | Example |
|----------|----------|---------|
| Error/recovery paths | **HIGH** | `except`, `finally`, rollback |
| Auth/permission | **HIGH** | Admin-only, role checks |
| Financial/money | **HIGH** | Billing, pricing calculations |
| Data mutation (write) | **MEDIUM** | Insert, update, delete paths |
| Feature branches | **MEDIUM** | A/B test paths, config-gated |
| Logging | **LOW** | Debug/info log lines |
| Debug-only code | **SKIP** | `if DEBUG:`, dev tools |
| Dead code | **DELETE** | Unreachable blocks |

## Branch vs. Line Coverage

Line coverage is misleading. Branch coverage tells the real story:

```python
# 100% line coverage (both lines executed):
def is_valid(age):
    return age >= 18     # line 1 — covered
# test: assert is_valid(20)

# But 50% branch coverage (only one branch tested):
# The `age < 18` branch is untested!
# test_is_valid_false() is missing!
```

```bash
# Enable branch coverage
pytest --cov=src/ --cov-report=term-missing --cov-branch
```

## Why 100% Is Usually Not the Goal

```text
Coverage  | Interpretation
----------|---------------
<60%      | Insufficient — high risk of regressions
60-80%    | Moderate — fill high-priority gaps
80-90%    | Good — focus on branch coverage, not line count
90-95%    | Solid — the remaining 5-10% is probably error paths
  100%    | Suspicious — are you testing dead code? Or assertion-less?
```

## Guardrails

- **Never chase the number.** 100% coverage with assertion-light tests is worse than 80% with rigorous assertions.
- **Don't delete uncovered code** without verifying it's truly dead (grep for callers).
- **Coverage is a floor, not a ceiling.** It tells you what's tested, not what's tested *well*.
- **Branch coverage > Line coverage.** Always use `--cov-branch`.

## Pitfalls

| Pitfall | Fix |
|---------|-----|
| Assertion-less tests bumping coverage | Every test must assert a behavior change, not just call the function |
| Adding tests for dead code | Delete dead code instead |
| Ignoring uncovered error branches | Error paths are where prod breaks — test them first |
| `# pragma: no cover` abuse | Use sparingly; it's a signal of untestable design |
| Coverage drop on refactor | Re-run coverage after refactor — new code is often uncovered |

## Verify / Checklist

- [ ] Coverage report generated with `--cov-branch` enabled
- [ ] High-priority gaps (error handlers, auth, money) are identified
- [ ] HTML report reviewed for any large uncovered blocks
- [ ] Uncovered error paths have tests (or documented reason for exclusion)
- [ ] No assertion-less coverage-stuffing tests
- [ ] CI enforces a coverage threshold (recommended: 80%)

Attached files

No attached files.