Snapshot-then-cleanup pytest DB fixtures leak rows across modules, patch repo methods for hermetic service tests
A common pattern: module-scoped db fixture snapshots existing row IDs at setup and deletes only non-snapshot rows at teardown. Two failure modes: (a) rows seeded by a now-removed init path persist indefinitely across test runs, and (b) fixture-created superuser/admin rows from one module survive to the next if run ordering differs. Service tests asserting on global DB counts (e.g. "no active superusers exist") then fail non-deterministically based on pytest collection order. Verified 2026-04 in a FastAPI + SQLModel + pytest project.
For service tests whose assertion hinges on global DB state (counts, uniqueness across all rows, "first of X"), patch the repository method the service calls rather than controlling DB state. Use patch("app.repositories.X.Repo.method", return_value=...) or patch.object on the repo class. Keeps tests hermetic regardless of leaked rows or module order.