It's 2pm. You need to ship a fix before end of day. You run the integration suite and it fails — not because your code is wrong, but because staging is down. Someone else's bad migration took it out an hour ago, and now you're in the "is it up yet" channel with six other engineers, watching a thread instead of shipping.

Sound familiar? This is the actual, daily cost of how most teams do integration testing. And it usually comes down to one of two bad options.

The two options, and why both are bad

Mock everything. Fast, deterministic, runs anywhere — and proves almost nothing. Your mock says Postgres returns rows in this shape. Real Postgres doesn't care what your mock thinks. The bug that matters — the one your mock can't see by definition — ships to production and pages someone at 2am.

Share a staging environment. Real dependencies, finally — but now you're queued behind whoever's deploying, debugging, or has just quietly broken it for everyone. It's slow, it's flaky in ways that have nothing to do with your code, and "is staging even up right now" becomes a real question you ask multiple times a week.