patterntypescriptMajor
Testcontainers for integration tests: run real databases in Docker per test suite
Viewed 0 times
testcontainersdocker databaseintegration test isolationpostgres testingreal database
docker
Problem
Integration tests that connect to a shared dev or CI database are flaky, non-parallel, and pollute each other's state. Mocking database drivers provides false confidence and fails to catch SQL dialect bugs, constraint violations, and migration issues.
Solution
Use
testcontainers (Node.js library) to spin up a real PostgreSQL, MySQL, or Redis container at the start of the test suite. The container gets a random port and is torn down after tests finish. Run migrations against it before the first test.Why
Each test suite gets an isolated, real database with no shared state. Tests run against the actual engine, catching query planner differences, index usage, and constraint behaviour that mocks miss. Containers start in 2-5 seconds — acceptable for a suite-scoped setup.
Gotchas
- Containers are suite-scoped, not test-scoped — each test must clean up rows it inserts or use transactions that roll back
- testcontainers requires Docker running locally and in CI; configure resource limits for CI runners
- Container startup adds 2-10 seconds of overhead; use beforeAll, not beforeEach
- In Jest, use --runInBand or jest-circus with proper teardown to avoid orphaned containers when workers are killed
Code Snippets
Testcontainers PostgreSQL setup in Jest
import { PostgreSqlContainer } from '@testcontainers/postgresql';
let container: StartedPostgreSqlContainer;
beforeAll(async () => {
container = await new PostgreSqlContainer('postgres:16').start();
await runMigrations(container.getConnectionUri());
}, 30_000);
afterAll(async () => {
await container.stop();
});Revisions (0)
No revisions yet.