# Mocks do not swallow the system under test

- **Pattern:** `ab-000296` (`ai-slop-test-theater.mock-hygiene.mocks-do-not-swallow-system-under-test`)
- **Severity:** high
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-000296
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-000296)

## Why it matters

When a test mocks every export from the file it is supposed to test, the test exercises the mock — not the production code. The function under test is never called. The test will pass even if the real implementation is completely deleted. This pattern appears in AI-generated tests where the model confuses "mock the dependencies of X" with "mock X itself." The test suite accumulates green results that have zero correlation with production behavior, creating false confidence. ISO-25010:2011 testability requires that tests exercise the actual system under test.

## Severity rationale

High because mock-saturated tests provide a false signal of correctness — the production code could be entirely broken while every test passes.

## Remediation

Mock external dependencies (databases, APIs, third-party SDKs), never the function being tested. The unit under test must be imported and called directly:

```ts
// Before: tests the mock, not the real calculateTax
import { calculateTax } from './tax'
vi.mock('./tax', () => ({ calculateTax: vi.fn(() => 100) }))
it('calculates tax', () => {
  expect(calculateTax(1000)).toBe(100) // always true
})

// After: real function runs, only its dependency is mocked
import { calculateTax } from './tax'
import { taxRateApi } from './tax-rate-api'
vi.mock('./tax-rate-api', () => ({ taxRateApi: { fetch: vi.fn(() => 0.1) } }))
it('calculates tax at 10% rate', async () => {
  const tax = await calculateTax(1000)
  expect(tax).toBe(100)
})
```

## Detection

- **ID:** `mocks-do-not-swallow-system-under-test`
- **Severity:** `high`
- **What to look for:** Walk all test files. For each test file, identify the file under test (typically the file with the same basename minus `.test`/`.spec`). Count all `vi.mock(`/`jest.mock(`/`sinon.stub(`/`td.replace(` calls in the test file. If the mock target IS the file under test (e.g., `import { foo } from './my-module'` is followed by `vi.mock('./my-module')` and the test then calls the mocked `foo`), the test is testing the mock — not the real code. Count test files where 100% of imports from the file under test are mocked.
- **Pass criteria:** 0 test files mock 100% of the system under test. Report: "X test files inspected, Y with reasonable mocking, 0 mock-saturated."
- **Fail criteria:** At least 1 test file mocks every export from the file it's supposedly testing.
- **Skip (N/A) when:** Project has 0 test files OR no mock library is in use.
- **Do NOT pass when:** A test file mocks every export from the file under test even if it then calls "the real function" — once the module is mocked, all subsequent imports get the mock.
- **Cross-reference:** For multi-test-framework drift detection, the Multi-Session Drift audit (`ai-slop-code-drift`) catches projects using both Jest and Vitest in parallel.
- **Detail on fail:** `"1 mock-saturated test: tests/users.test.ts mocks './users' completely — every function it calls is a mock, the real code is never executed"`
- **Remediation:** When the test mocks the system under test, it's testing the mock, not the code. Mock external dependencies (databases, APIs, third parties), not the unit being tested:

  ```ts
  // Bad: mocks the function being tested
  import { calculateTax } from './tax'
  vi.mock('./tax', () => ({
    calculateTax: vi.fn(() => 100),
  }))
  it('calculates tax', () => {
    expect(calculateTax(1000)).toBe(100)  // tests the mock, not the real fn
  })

  // Good: mock dependencies, test the real function
  import { calculateTax } from './tax'
  import { taxRateApi } from './tax-rate-api'
  vi.mock('./tax-rate-api', () => ({
    taxRateApi: { fetch: vi.fn(() => 0.1) },
  }))
  it('calculates tax at 10% rate', async () => {
    const tax = await calculateTax(1000)  // real function, mocked dependency
    expect(tax).toBe(100)
  })
  ```

## External references

- iso-25010 maintainability.testability

Taxons: code-quality

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