Sharing a database container across test classes saves startup time but requires data and transaction isolation between concurrent tests.
Spring Testcontainers parallel tests: one database can leak another test's rows
A shared port is not a private database
Two test classes can reuse the same Spring context and PostgreSQL container. If both insert order 47 and one truncates a table in @BeforeEach, the result depends on scheduling. A rollback annotation on the test method does not clean work done by a worker thread or a separate transaction. Thread-bound transactions and new transactions make that leak easy to miss.
Pick one isolation mechanism
Use unique tenant and command IDs per test when assertions can filter by them. For tests that verify global counts or migration state, use a separate database, schema, or serialized suite; do not rely on a random sleep. Keep cleanup tied to the fixture it created and never truncate shared tables while parallel tests run. Migrations should be applied once by the chosen context owner, not raced by every test class.
Run the hostile schedule
Start both classes in parallel. Pause one test after insert, let the other complete its cleanup, then release the first and assert its row remains. Repeat with a worker that commits outside the test transaction. The code documents unique IDs; CI must still run the actual parallel schedule for this guarantee.
Implementation sketch
String tenantId = "test-" + UUID.randomUUID();
ledger.insert(tenantId, "order-47", 23);
assertEquals(23, ledger.totalForTenant(tenantId));
// Cleanup only rows owned by tenantId.Cost and verification
Unique test data is cheap but can grow a long-lived container's tables. Separate schemas or containers cost startup time and memory but give stronger isolation for global-state tests.
Common Mistakes
- Do not truncate a shared database from a parallel test's setup or cleanup.
- Do not assume test rollback covers @Async work or REQUIRES_NEW scopes.
- Do not claim parallel safety from a sequential local run.
Read next
Spring Boot Testcontainers: service connections and cached-context lifecycle, Spring Boot Testcontainers and Flyway: let one owner prepare the test schema, Spring imperative transaction: a worker thread does not inherit it, Spring REQUIRES_NEW: budget the second database connection.
