intermediate 2 min answer

A team replaces every in-process fake with the real dependency in a container, and defect escape rate falls. What have they bought and when does the bill arrive?

testcontainersintegration-testsci-costisolationfeedback-loop
Show the full answer Hide the answer

What is gained

A class of defect that a fake cannot produce, because a fake encodes your beliefs about the dependency. SQL dialect and migration behaviour, driver-level type coercion, collation and timezone semantics, transaction isolation, unique-constraint races, JSON serialisation differences, and the broker's actual redelivery rules. These are the defects that reach production from teams with excellent unit coverage, and they are why the change works.

What is paid

  • Wall-clock. A container start is typically 0.5 to 3 seconds, and naive designs pay it per test class. At 400 such tests that alone is minutes to tens of minutes.
  • CI resources and parallelism. Each worker needs its own container set, so memory per agent bounds parallelism, and the suite stops getting faster with more workers.
  • A new flake surface: readiness races, port binding, image pulls, disk pressure, the container clock.
  • The laptop. If the suite no longer runs locally, the feedback loop moves to CI and every iteration costs a push.

When the bill arrives

When the suite crosses roughly ten minutes, developers stop running it before pushing — and that is the moment the defect-escape benefit starts eroding, because the tests now find problems after merge instead of before. The curve is not gradual; it is a behaviour change at a threshold.

How to keep the gain and defer the bill

  • One container per suite or per worker, not per test, with isolation by data instead: a schema, a database or a tenant per worker, created in milliseconds and dropped at the end.
  • Reusable containers between local runs, so the cost is paid once per day rather than per run.
  • Draw the boundary by ownership. Real dependency for the stores you own and whose semantics you depend on; a contract test plus a fake for third-party HTTP, where a container is just someone else's mock with a Dockerfile.
  • Keep a fast lane. A sub-minute subset that runs on save, with the full set on push.

When not to do this at all

Pure logic — pricing rules, parsers, state machines, scheduling algorithms — gains nothing from a container and pays the whole cost. If a test would pass identically against an in-memory implementation, the in-memory one is not a weaker test, it is the same test at a hundredth of the price.