CodeOath
← All posts
Architecture & Patterns70 min total · 17 parts

Microservices vs. Monolith: Architecture Patterns and Trade-offs

Contents — Part 12 of 17: Testing Challenges
Part 12 of 17 · ~1 min

Testing Challenges

A monolith's integration tests exercise real code paths across modules within one process — fast, deterministic, no network involved. Once those modules become separately deployed services, testing a real interaction between two of them means either running both together (slow, and now you need test infrastructure for however many services are actually involved) or convincingly faking one side.

Contract testing is the common middle ground: rather than running a full integration environment, each consumer of a service defines the exact contract it expects (a request shape and the response shape it needs back), and that contract is verified independently against both sides — the consumer's tests run against a mock built from the contract, and the provider's tests separately verify it actually satisfies that same contract. This catches a breaking API change without needing every consumer and provider running together in the same test suite, at the cost of the contract itself needing to be kept honest and up to date as both sides evolve.

End-to-end tests that exercise a real request through several actual services still have real value (some categories of bug only show up when real services interact) but are slower and more prone to unrelated flakiness (any one of several services having a bad moment fails the whole test) than either unit tests or contract tests — the same test-pyramid trade-off from CI/CD Pipelines Explained, just with an added "which of five services is actually at fault when this fails" layer on top.