# Unit test coverage above 60%

- **Pattern:** `ab-002143` (`code-quality-essentials.testing.unit-coverage`)
- **Severity:** medium
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-002143
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-002143)

## Why it matters

Having test files is necessary but not sufficient — a test suite that only renders components without crashing and ignores business logic in `src/lib/` provides false confidence. ISO 25010:2011 §6.5.5 requires that reliability verification covers the critical paths. A payment calculation function, a permission check, or a data transformation that is never tested will eventually receive a change that breaks it; without a coverage gate, no one learns about it until a user reports a wrong order total or a privilege escalation.

## Severity rationale

Medium because low unit coverage means business logic changes ship without regression verification, turning every refactor into an undetected risk for production correctness.

## Remediation

Prioritize business logic over UI rendering — pure functions in `src/lib/`, `src/utils/`, and `src/services/` are the highest-return coverage targets. Enforce a minimum in your test config:

```typescript
// vitest.config.ts
import { defineConfig } from 'vitest/config'
export default defineConfig({
  test: {
    coverage: {
      provider: 'v8',
      thresholds: {
        lines: 60,
        functions: 60,
        branches: 50,
      },
      exclude: ['**/*.config.*', 'src/app/**', 'src/components/**'],
    },
  },
})
```

Run `npx vitest run --coverage` to see your current baseline before writing new tests.

## Detection

- **ID:** `unit-coverage`
- **Severity:** `medium`
- **What to look for:** Estimate coverage by comparing the number of test files to source files containing business logic. Focus on `src/lib/`, `src/utils/`, `src/services/`, `src/hooks/`, and similar directories — not UI component files or page files, which are harder to unit test and often better covered by integration tests. Look for coverage configuration in `jest.config.*` or `vitest.config.*` — presence of `coverageThreshold` (Jest) or `thresholds` (Vitest) means the project enforces minimum coverage. Check CI workflows for a coverage step. A project with many test files but only testing trivial components (renders without crashing) while leaving core data transformation logic untested still fails this check.
- **Pass criteria:** At least 60% of business logic modules (lib, utils, services, hooks) have corresponding tests with substantive assertions. Coverage tooling is configured or the project clearly covers the majority of critical paths.
- **Fail criteria:** Few or scattered tests; core business logic files have no corresponding tests; test-to-source ratio below 30% for non-trivial codebases.
- **Skip (N/A) when:** No test files exist (covered by the previous check).
- **Detail on fail:** `"Estimated coverage below 60%; core logic in src/lib/ appears untested; only UI smoke tests found"` (adjust message to what you found)
- **Remediation:** Prioritize testing business logic over UI rendering. Focus on pure functions first — they're easiest to test and carry the most risk when broken:

  ```typescript
  // vitest.config.ts — enforce coverage minimums
  import { defineConfig } from 'vitest/config'
  export default defineConfig({
    test: {
      coverage: {
        provider: 'v8',
        thresholds: {
          lines: 60,
          functions: 60,
          branches: 50,
        },
        exclude: ['**/*.config.*', 'src/app/**', 'src/components/**'],
      },
    },
  })
  ```

  Run `npx vitest run --coverage` to see your current baseline before writing tests.

## External references

- iso-25010 maintainability.testability

Taxons: code-quality

HTML version: https://auditbuffet.com/patterns/ab-002143
