Ba cách, đánh đổi khác nhau:
1. Transaction rollback mỗi test — mở transaction trước test, ROLLBACK sau test. Nhanh nhất, không cần dọn tay.
beforeEach(() => db.query('BEGIN'))
afterEach(() => db.query('ROLLBACK'))Giới hạn: code dưới test phải dùng đúng connection đó, nên hỏng ngay khi app tự mở transaction riêng, dùng connection pool, hoặc test đi qua HTTP tới một process khác. Không kiểm chứng được commit thật (trigger, deferred constraint).
2. Truncate giữa các test — TRUNCATE ... RESTART IDENTITY CASCADE các bảng nghiệp vụ, giữ lại bảng tham chiếu. Đúng với mọi kiến trúc vì dữ liệu được commit thật, nhưng chậm hơn và không cô lập được khi chạy song song trên cùng một database.
3. Schema (hoặc database) riêng mỗi worker — cách duy nhất thực sự chạy song song an toàn. Test runner cấp WORKER_ID, mỗi worker trỏ vào schema riêng đã migrate sẵn.
const schema = `test_${process.env.VITEST_WORKER_ID}`Cách chọn trong thực tế: rollback cho test gọi thẳng repository/service trong cùng process; schema riêng mỗi worker + truncate trong worker khi test đi qua HTTP hoặc cần commit thật.
Điều phải tránh là dựa vào thứ tự test hay vào dữ liệu do test trước để lại — đó là nguồn của test flaky khó tìm nhất.