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.
Medium because unbalanced setup causes intermittent failures and cross-test contamination that are difficult to diagnose, though immediate test runs may still pass.
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.
ID: ai-slop-test-theater.mock-hygiene.setup-teardown-balanced
Severity: medium
What to look for: Walk all test files. For each file, count all beforeAll(/beforeEach( calls AND all afterAll(/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 no afterAll(/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.