Integration Testing Guide: Prove the Pieces Work Together
Learn practical integration testing strategies for APIs, databases, queues, external services, test data, CI pipelines, and reliable confidence.
Integration tests check real collaboration
Unit tests prove small pieces of logic. Integration tests prove that important pieces work together: API routes with database access, services with queues, authentication with permissions, or application code with a cache. They catch failures that mocks often miss, such as wrong SQL, missing migrations, serialization mistakes, configuration drift, and transaction behavior.
The goal is not to make every test full-stack. The goal is to test boundaries where integration risk is real. If a feature depends on database constraints, background jobs, and API responses, at least some tests should exercise those connections.
Design tests around confidence
Good integration tests create the data they need, perform an action through a realistic interface, and assert behavior that matters. They should be isolated enough to run repeatedly without depending on old shared records. Test data management is often the difference between a trusted suite and a flaky one.
- Use a test database with migrations that match production.
- Reset or isolate data between tests.
- Replace truly external services with stable fakes when needed.
- Keep critical integration tests in CI so configuration mistakes are caught early.
Control cost and scope
Integration tests are slower than unit tests, so choose scope deliberately. Test the important flows: signup, permissions, payment state, publishing, search indexing, and data consistency. Avoid duplicating every unit test through a slower path.
A healthy test strategy has layers. Unit tests cover detailed logic, integration tests cover collaboration, and end-to-end tests cover user journeys. Integration tests are the middle layer that proves the system is wired correctly before users find the gap.
Keep integration tests diagnosable
When an integration test fails, the output should show which boundary broke. Was it validation, database setup, authentication, queue publishing, serialization, or an external fake? Clear setup and assertion messages save time because integration failures can otherwise look like broad system failures.
Use logs and test artifacts deliberately. Capturing request IDs, SQL errors, response bodies, and queue messages for failed tests can make the difference between a quick fix and a long debugging session in CI.
Use realistic infrastructure where it matters
Some integration bugs appear only with the real database engine, queue, or cache. In-memory substitutes are fast, but they may hide SQL behavior, transaction rules, serialization differences, or connection problems. Use realistic dependencies for the tests that protect critical behavior.
Balance realism with speed. Not every test needs the full stack, but the important boundaries should be tested against something close enough to production to catch real wiring mistakes.
Review integration coverage by risk
As a system grows, revisit which integrations deserve tests. New payment paths, permission rules, background jobs, search indexing, and migration-heavy changes may need additional coverage. Old low-value tests may be removed or moved to a slower schedule. The suite should follow the risk profile of the product, not remain frozen around the first version of the app.