TM
← BACK TO JOURNALENGINEERING

Why I stopped mocking the database

6 MIN READ · 2026-07-02

For years my test suites mocked the database layer. It made tests fast, it made them deterministic, and every testing tutorial I'd ever read told me it was the right call. Then a migration shipped that passed every test and broke production within an hour.

The mock had drifted from the real schema months earlier — a column type change, nothing dramatic — and nothing caught it because the mock was hand-maintained and nobody updates a mock on a schema change unless a test actually fails. The tests couldn't fail, because they weren't testing the real thing.

The fix wasn't clever: integration tests now run against a real database in a container, seeded fresh per test run.

# conftest.py
import pytest
from testcontainers.postgres import PostgresContainer

@pytest.fixture(scope="session")
def db():
    with PostgresContainer("postgres:16") as postgres:
        run_migrations(postgres.get_connection_url())
        yield postgres

Slower, yes. But the only tests that would have caught the actual incident are the ones that touch the real thing.

The lesson generalizes past databases. Any mock is a claim about how a dependency behaves, frozen at the moment you wrote it. If nothing forces that claim to stay true, it quietly stops being true, and your tests start testing the mock instead of the system.