Setup and teardown are balanced
Why it matters
When a beforeAll or beforeEach hook creates database rows, writes files, or inserts records without a matching afterAll/afterEach to remove them, test data accumulates across runs. Subsequent test runs operate on a progressively more polluted database: unique-constraint violations start appearing, count assertions become wrong, and test order begins to matter. This makes the test suite flaky and eventually unmaintainable. ISO-25010:2011 recoverability requires that test runs return the system to a clean state.
Severity rationale
Medium because unbalanced setup causes intermittent failures and cross-test contamination that are difficult to diagnose, though immediate test runs may still pass.
Remediation
Every beforeAll/beforeEach that creates state needs a corresponding afterAll/afterEach that removes it. Track created entity IDs in the outer scope and delete them in teardown:
describe('UserAPI', () => {
let userId: string
beforeAll(async () => {
const user = await prisma.user.create({ data: { email: 'test@example.com' } })
userId = user.id
})
afterAll(async () => {
await prisma.user.deleteMany({ where: { id: userId } })
})
it('retrieves the user', async () => {
const found = await prisma.user.findUnique({ where: { id: userId } })
expect(found?.email).toBe('test@example.com')
})
})
For simpler isolation, run each test inside a database transaction and roll it back in afterEach — no manual tracking required.
Detection
-
ID:
setup-teardown-balanced -
Severity:
medium -
What to look for: Walk all test files. For each file, count all
beforeAll(/beforeEach(calls AND allafterAll(/afterEach(calls. For each test file with setup hooks containing state-creating operations (.create(,prisma.X.create(,db.X.insert(,fs.writeFile(,fs.mkdir(), verify there is a corresponding teardown hook in the same file (or imported from a setup helper). -
Pass criteria: 100% of files with state-creating setup hooks have corresponding teardown hooks. Report: "X files with setup hooks, Y with teardown, 0 unbalanced."
-
Fail criteria: At least 1 test file has a
beforeAll(/beforeEach(that creates state but noafterAll(/afterEach(to clean it up. -
Skip (N/A) when: No test files contain setup hooks with state creation.
-
Detail on fail:
"1 unbalanced setup: tests/api.test.ts has beforeAll() that calls prisma.user.create() but no afterAll() to delete created users — tests pollute the database between runs" -
Remediation: Without teardown, tests pollute the database and affect subsequent runs. Always pair setup with teardown:
describe('UserAPI', () => { let userId: string beforeAll(async () => { const user = await prisma.user.create({ data: { email: 'test@example.com' } }) userId = user.id }) afterAll(async () => { await prisma.user.delete({ where: { id: userId } }) }) it('finds the user', async () => { const found = await prisma.user.findUnique({ where: { id: userId } }) expect(found).toBeTruthy() }) })Better: use a transaction-rollback pattern so setup is implicitly cleaned up.
External references
- iso-25010:2011 · reliability.recoverability — Recoverability (reliability sub-characteristic)
Taxons
History
- 2026-04-18·v1.0.0·Initial import from ai-slop-test-theater·automated