Testcontainers vs Mocks: Real DB Testing Wins

Wasted Fridays mocking databases that crumble in prod? I've been there. Testcontainers spins up real ones — slower, messier, but brutally honest.

Testcontainers Over Mocks: Trading Speed for Sanity in Database Tests — theAIcatchup

Key Takeaways

  • Testcontainers uses real Docker containers to test databases accurately, catching issues mocks ignore.
  • CI setup is painful with permissions and disk issues, but trades for reliable code.
  • Prefer suite-wide containers with per-test schemas for balance of speed and truth.

Pager screaming at 3 AM. Production database flatlines on a query your tests swore was golden.

That’s the mock life — fast greens, brutal lies. Testcontainers? It’s the gritty alternative I’ve sworn by for years, firing up actual Docker containers for your Postgres or whatever hell your data layer lives in. No more faking it; just raw, unfiltered reality.

And here’s the thing: in twenty years chasing Silicon Valley’s testing unicorns, I’ve seen this movie before. Back in the early 2000s, we ditched brittle unit tests for integration ones, only to rediscover the wheel with mocks a decade later. Testcontainers feels like that pivot — painful setup, but it forces you to confront the database as it truly is, not your rosy assumptions.

“Mocks don’t test your code, they test your assumptions about your code. And usually, your assumptions are wrong.”

Spot on. I’ve burned entire Fridays crafting “perfect” mocks for GetByID, nailing down every struct, every edge case. Then bam — prod crashes on a Postgres constraint I never dreamed of. Mocks lie sweetly, whispering “all good” while ignoring SQL syntax goofs, JSONB quirks, foreign keys that bite back.

Why Testcontainers Actually Catches the Bugs Mocks Miss?

Real containers don’t care about your optimism. Spin up postgres:16-alpine, hook your repo to it, and watch it fail on real violations — unique constraints, connection timeouts, the works. Sure, it takes ten seconds to boot versus mock’s blink, but that’s ten seconds of truth versus ten minutes of deception.

Look, I don’t restart for every test. Insanity. One Postgres instance at suite start, fresh schemas or dbs per test via migrations. Clean, reliable. Your hand-rolled SQL? It either flies or dies right there, no staging deploy needed.

But — and it’s a big but — CI turns into a warzone. GitHub Actions? Docker-in-Docker nightmares, /var/run/docker.sock permission dances, runners choking on disk space mid-pull. I’ve lost mornings to hanging pipelines, cursing alpine images that won’t cache.

It’s a trade: flaky CI for bulletproof code. Worth it? Damn right, especially when you’re knee-deep in custom SQL where assumptions kill.

Is Testcontainers Worth the CI Pain for Devs?

Short answer: yes, if your data layer’s complex. Simple CRUD? Mocks might suffice. But anything with real queries, constraints, versioning? Containers win.

My bold call — one you won’t find in the Testcontainers docs: in five years, this’ll be table stakes, baked into frameworks like Go’s testify or Rust’s sqlx. No more “mock everything” dogma; we’ll laugh at it like we do vi over vim wars. The PR spin from mock-lovers calls it “overkill,” but they’re the ones paging at night.

Take this snippet — straight from the trenches:

pgContainer, err := postgres.RunContainer(ctx,
    testcontainers.WithImage("postgres:16-alpine"),
    postgres.WithWaitStrategy(
        wait.ForLog("database system is ready to accept connections").
            WithOccurrence(2).
            WithStartupTimeout(5*time.Second),
    ),
)

Crude? Yeah. Effective? Hell yes. Two minutes for the suite, not two seconds of fantasy.

Local dev’s no picnic either — Docker on Mac crawls like molasses. Windows? Pray. But tools evolve: remote containers, lighter images, better orchestration. It’s getting there.

Never going back to heavy mocking for repos. Risk’s too high. That 3 AM page? Not on my watch anymore.

The skeptic in me whispers caveats. Not for every layer — pure business logic still loves mocks. And if your team’s green on Docker, expect onboarding friction. But for database truth? Unbeatable.

Wrapping the cynicism: Testcontainers isn’t hype. It’s the hammer you need when nails hide in shadows.


🧬 Related Insights

Frequently Asked Questions

What is Testcontainers and how does it work?

Testcontainers launches real Docker containers (like Postgres) for tests, giving you a live database to query instead of faked responses.

Testcontainers vs mocks: which is better for Go database tests?

Testcontainers for anything real-world; mocks for speed on trivial stuff. Prioritize truth over green lies.

How do I set up Testcontainers in GitHub Actions?

Use Docker-in-Docker or remote runners, mount docker.sock carefully, and cache images to dodge hangs — expect tweaks.

Marcus Rivera
Written by

Tech journalist covering AI business and enterprise adoption. 10 years in B2B media.

Frequently asked questions

What is Testcontainers and how does it work?
Testcontainers launches real Docker containers (like Postgres) for tests, giving you a live database to query instead of faked responses.
Testcontainers vs mocks: which is better for Go database tests?
Testcontainers for anything real-world; mocks for speed on trivial stuff. Prioritize truth over green lies.
How do I set up Testcontainers in GitHub Actions?
Use Docker-in-Docker or remote runners, mount docker.sock carefully, and cache images to dodge hangs — expect tweaks.

Worth sharing?

Get the best AI stories of the week in your inbox — no noise, no spam.

Originally reported by Dev.to

Stay in the loop

The week's most important stories from theAIcatchup, delivered once a week.