DOTNET Contents

Testcontainers Basics (Real Dependencies)

Testcontainers gives you real dependencies (Postgres, Redis, RabbitMQ) in CI without shared staging flakiness. Use it to catch config, migrations, and query issues before production—while keeping tests isolated and repeatable.

On this page

Production incident

A service ships with a migration that fails only on a clean database. Local dev DBs were dirty and already had the tables. CI used mocks, so it never caught it. Production deploy fails and the app cannot start. Another time, Redis config differs between local and production and caching breaks. These are integration failures. Testcontainers catches them by running real dependencies in tests.

Symptoms

  • CI passes, but deployment fails on environment setup or migrations.
  • Works on developer machines, fails on clean environments.
  • Subtle dependency behavior differences (collations, timeouts, auth) cause runtime bugs.

Root causes

  • Mocking infra dependencies hides real failure modes.
  • Using shared test DBs introduces state leakage and flakiness.
  • No automated verification of migrations against a clean DB.

Anti-pattern

  • Shared staging DB for CI tests.
  • Hard-coded connection strings and ports.
  • Leaving containers running and relying on order-dependent state.

Correct pattern

  • Spin up dependencies per test suite with Testcontainers.
  • Apply migrations automatically in the test setup.
  • Isolate data per test and tear down reliably.

Practical approach

// Conceptual setup: start container once for test collection, then create DbContext with container connection string
// Exact code depends on your Testcontainers library package and versions.

The important part is not the code snippet. The important part is the operational behavior: clean dependency, clean schema, deterministic runs.

Security and performance impact

  • Security: ensures auth/SSL/config patterns are validated. Don’t bake real secrets into tests.
  • Performance: containers cost time. Use them for integration suites, not for every unit test. Cache images in CI runners.

Operational notes

  • CI: run Testcontainers suites on PRs or at least on main merges. Keep unit tests fast and separate.
  • Rollout: every migration should be validated against a clean container DB before release.
  • Rollback: if a migration breaks, CI should have caught it. If not, add a migration test immediately.

Checklist

  • Integration tests use real dependencies via containers.
  • Migrations are applied on clean databases in CI.
  • Tests are isolated and teardown is reliable.
  • No shared infra state across test runs.
  • Containers are used where they add real signal, not everywhere.