# Assertion density is reasonable per test

- **Pattern:** `ab-000294` (`ai-slop-test-theater.assertion-quality.assertion-density-reasonable`)
- **Severity:** medium
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-000294
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-000294)

## Why it matters

When the average assertion-to-test-block ratio falls below 1.0, the majority of test blocks have no assertions at all. This isn't a style issue — it means most of your test suite is exercising code paths without checking outcomes. Coverage tools will report lines as "covered" while the behavior of those lines remains completely unverified. ISO-25010:2011 testability requires that tests can detect deviations from specified behavior; a test block with no assertion can never detect anything.

## Severity rationale

Medium because a low ratio signals systemic assertion absence rather than isolated gaps, but individual test blocks vary in severity depending on what they cover.

## Remediation

Audit each `it`/`test` block and add at least one `expect` call that checks the actual output or state change produced by the code under test. Aim for a ratio above 1.0 project-wide:

```ts
// Before: exercises code, verifies nothing
it('finds user by id', async () => {
  await service.find('user-1')
})

// After: asserts identity and shape
it('finds user by id', async () => {
  const found = await service.find('user-1')
  expect(found?.id).toBe('user-1')
  expect(found?.email).toBeDefined()
})
```

## Detection

- **ID:** `assertion-density-reasonable`
- **Severity:** `medium`
- **What to look for:** Walk all test files. For each test file, count all `it(`/`test(`/`Deno.test(` blocks AND count all assertion calls (any pattern from check 1). Compute the ratio: `assertion_calls / test_blocks`. A ratio of less than 1.0 means many test blocks have zero assertions on average. Report files where the ratio is below 1.0.
- **Pass criteria:** Average assertion-per-test ratio across the project is at least 1.0. Report: "X test blocks across Y test files, Z total assertions, ratio: A.A per test."
- **Fail criteria:** Average assertion ratio is below 1.0 — most tests have no assertions.
- **Skip (N/A) when:** Project has 0 test files.
- **Detail on fail:** `"Average assertion ratio is 0.4 — 50 test blocks across 10 files have only 20 total assertions. Most tests have no assertion."`
- **Remediation:** A test without an assertion is just a code-coverage line counter — it exercises the function but doesn't verify anything. Add at least 1 assertion per test:

  ```ts
  describe('UserService', () => {
    it('creates a user', async () => {
      const user = await service.create({ email: 'a@b.com' })
      expect(user.id).toBeDefined()
    })

    it('finds a user by id', async () => {
      const found = await service.find('user-1')
      expect(found?.id).toBe('user-1')
    })
  })
  ```

## External references

- iso-25010 maintainability.testability

Taxons: code-quality

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