概述
测试换个顺序就失败,说明用例之间共享了状态,通常是数据库:一个用例留下数据,另一个假设表是空的,结果取决于调度顺序。每个用例前清空所有表虽然可行,但很慢,而且在并行 worker 下依然脆弱。正确目标是让每个测试都有确定的起点,并由测试框架保证,而不是靠约定。
步骤
- 建立一次模板数据库并执行迁移,模板中只有结构没有数据:
createdb app_template && DATABASE_URL=postgres://localhost/app_template pnpm migrate
- 每个 worker 从模板复制一个数据库:
const dbName = `app_test_${process.env.VITEST_POOL_ID ?? process.pid}`;
await admin.query(`CREATE DATABASE ${dbName} TEMPLATE app_template`);
- 每个测试用事务包裹,结束后回滚,不再需要
TRUNCATE:
beforeEach(async () => { client = await pool.connect(); await client.query('BEGIN'); });
afterEach(async () => { await client.query('ROLLBACK'); client.release(); });
- 把事务绑定的 client 注入仓储层,确保被测代码使用同一个连接;若 handler 直接从连接池取连接,回滚无法隐藏它的写入。
- 在全局 teardown 中删除各 worker 的数据库。
验证
开启并行运行后所有测试应通过,重复运行结果一致。故意让测试 A 插入用户、测试 B 统计用户数并断言为零,两者在任意顺序下都应通过。检查每个测试结束后表中没有新增行,且 worker 之间的数据互不可见。
注意事项
- 事务回滚无法隔离其他连接上的写入、序列值与会话级状态。
- 序列在回滚后仍会前进,不要断言精确 id,应使用返回的 id。
- 模板数据库不能有活动连接,复制前需断开连接池。
- 会话级 advisory lock 不会随回滚释放,应使用
pg_advisory_xact_lock。 - 每个 worker 一个数据库会占用磁盘与连接数,必须清理,否则本地会逐渐耗尽资源。