Mocks do not swallow the system under test
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:
// 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 allvi.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 byvi.mock('./my-module')and the test then calls the mockedfoo), 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:
// 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:2011 · maintainability.testability — Testability (maintainability sub-characteristic)
Taxons
History
- 2026-04-18·v1.0.0·Initial import from ai-slop-test-theater·automated