Integration Testing (WebApplicationFactory, TestContainers) (EN)
Integration tests validate that components work together (HTTP pipeline, DI, serialization, database, messaging), without needing full end-to-end UI.
At lead level, the key is to design a test strategy that is:
- Representative (real dependencies where it matters)
- Reliable (no flakes)
- Fast enough for CI (parallelizable, scoped)
1) When you should prefer integration tests
Integration tests are best for:
- ASP.NET Core startup + middleware + routing
- Model binding, validation, filters
- Serialization behavior (System.Text.Json options)
- EF Core mapping, migrations, query translation
- Authentication/authorization policies (without real identity providers)
Unit tests are still best for:
- pure domain rules
- complex algorithms
- small deterministic transformations
2) The “right realism” principle
Make integration tests realistic where bugs happen:
- Use a real DB engine for query translation/perf behavior (TestContainers)
- Use in-process server (
WebApplicationFactory) for HTTP pipeline
Avoid realism that adds little value:
- real external network
- real third-party APIs (use contract tests or stubs)
3) WebApplicationFactory mental model
WebApplicationFactory<TEntryPoint> (ASP.NET Core) hosts your app in-memory so your tests can call endpoints with HttpClient.
Typical hooks:
- Override configuration (connection strings, flags)
- Replace services for testing (e.g., fake email sender)
- Ensure DB is created/migrated
4) TestContainers strategy
Use TestContainers when you need:
- real SQL Server/Postgres behavior
- realistic isolation levels
- real indexes and query plans
Lead-level pitfalls
- Slow startup: mitigate with reusable containers per test collection.
- Port collisions: let TestContainers pick ports.
- Parallelization issues: isolate DB per test or per collection.
5) Test data strategy
At scale, tests fail because data management is chaotic.
Recommended pattern:
- Use Arrange helpers + builders
- Make tests self-contained (create data they need)
- Reset DB between tests using:
- transactions + rollback (fast, but tricky with async/background work)
- truncate tables (deterministic, sometimes slower)
- recreate schema per test collection
6) Flake-proofing integration tests
Common sources of flakiness:
- time-based logic → inject clock
- async eventual consistency → poll with timeouts and clear contracts
- random ports / reused state
- parallel tests sharing data
Rule of thumb:
- If you must poll, poll for a bounded time and include meaningful diagnostics.
7) What “good coverage” looks like
A TL-friendly integration suite usually covers:
- health endpoint
- one “happy path” per critical use case
- one validation failure path
- one authorization failure path
- one persistence scenario that validates mapping + constraints
Avoid building a second, slower unit test suite.
8) Interview angle
Be ready to discuss:
- why you choose TestContainers over EF InMemory
- how you keep integration tests fast
- how you manage DB state
- how you structure test suites (collections, parallelization)
9) Review checklist
- [ ] Integration tests run in CI within acceptable time (target: minutes).
- [ ] TestContainers used only where needed (DB/query translation).
- [ ] Data isolation strategy is documented and consistent.
- [ ] No hidden dependencies on developer machine state.
- [ ] Parallelization is controlled (collections) and deterministic.