Introduction
Even if all your unit tests pass and your software is deployed, a user can still face problems with your service or payment gateways due to errors in the API response. Technically, there was nothing wrong with your product, but the failure was in the space between components. That space is what integration testing covers.
This ultimate integration testing guide walks through what integration testing is, how it works, and its main approaches, and gives you a practical integration testing workflow you can run in CI.
What Is Integration Testing
Integration testing is a form of software testing that checks that two or more components work correctly when combined. A component can be a class, a module, a service, a database, or a third-party API. The point is to test the handoff between each component in order to make sure that the connection works correctly.
Think of an online store where a unit test verifies that your “calculate total” function correctly adds up the prices of items in a cart. An integration test goes a step further. It verifies that once the total is calculated, your checkout service successfully passes that order information to the payment gateway and receives a “success” confirmation back.
This test case ensures that your system components aren’t just working individually but are actually communicating and completing the transaction as expected.
What Does Integration Testing Actually Verify
Most integration bugs cluster around a small set of boundaries. A useful integration suite targets these directly:
- Database connections and transaction boundaries. Integration testing verifies database connections and transaction boundaries, such as the ORM mapping matching the schema, rollbacks happening when a write fails halfway through, and migrations running cleanly against a fresh database.
- REST and gRPC API request/response contracts. Integration testing for REST and gRPC API request/response contracts verifies that consumers send data that matches provider expectations, ensures that clients robustly handle empty nullable fields, and confirms that status codes and error bodies are properly managed.
- Message broker event publishing and consumption. Integration testing at this boundary verifies that the producer serializes events in a format the consumer can deserialize and confirms how the system handles scenarios such as redelivery, ordering changes, or poison messages.
- File system read/write operations. Integration testing for file system read/write operations verifies that path handling, encoding, permissions, and cleanup behave consistently across environments, ensuring that tests interacting with the actual disk catch issues that stub-based tests might miss.
- External SaaS and third-party service adapters. Integration testing for external SaaS and third-party service adapters tests the APIs and services of payment providers, email, and auth providers. You rarely call the live service in a test, but you do need to verify your adapter handles its real response shapes, rate limits, and failure modes.
Types of Integration Testing
The classic integration testing strategies come from an era of monolithic systems with strict module hierarchies. They still apply, especially when you’re integrating a large codebase or replacing a legacy system piece by piece.
Big Bang Integration Testing
In big bang integration testing, all components are built, then integrated and tested at once. It’s simple to plan and requires no stubs or drivers. The drawback is obvious: when something fails, isolating the cause across dozens of simultaneously connected modules is slow. Big bang integration testing works for small systems with few components, but it’s not scalable.
Top-Down Integration Testing
In top-down integration testing, testing starts at the highest-level module and works downward. Lower-level modules that aren’t ready yet are replaced with stubs that return canned responses. This approach validates the main control flow early and lets you demo working features before the whole system exists. The cost is writing and maintaining stubs, and the lowest-level modules (often the ones handling data and I/O) get tested last.
Bottom-Up Integration Testing
Bottom-up integration testing is the reverse of top-down. Testing starts with the lowest-level modules and moves up. Higher-level modules that don’t exist yet are simulated with drivers that call the modules under test. Bottom-up integration testing surfaces problems in foundational components early, which is useful when the data layer is the riskiest part. The trade-off is that the top-level flow, the thing users actually see, is validated last.
Sandwich (Hybrid) Integration Testing
In sandwich or hybrid integration testing, top-down and bottom-up run in parallel and meet in the middle. Large teams use this to work on both ends simultaneously. It’s the most flexible approach and the most demanding. You need both stubs and drivers, and the middle layer where the two efforts converge is often the hardest to test properly.
In modern microservice environments, these categories often blur. You’re more likely to think in terms of which service boundary you’re testing than which direction you’re integrating.
Where Integration Testing Fits in the Software Testing Pyramid
The testing pyramid puts many fast unit tests at the base, fewer integration tests in the middle, and a small number of end-to-end tests at the top. The shape reflects cost. Higher-level tests are slower to run, harder to maintain, and more likely to fail for reasons unrelated to the code under test.
Integration tests sit in the middle because they’re the best compromise. They catch real wiring bugs without the fragility of a full browser-driven flow.
Integration Testing vs Unit Testing
Unit testing focuses on isolated functions or classes using mocked or stubbed dependencies, typically running in milliseconds to catch specific logic errors that suggest the code itself is incorrect. In contrast, integration testing examines how two or more components interact by utilizing real dependencies like databases, APIs, or message brokers. This process is slower, taking seconds to minutes to run, and is specifically designed to identify contract and configuration errors, highlighting instances where individual components fail to fit together correctly.
Integration Testing vs End-to-End Testing
End-to-end (E2E) tests exercise a complete user journey through the real system: browser, frontend, backend, database, and everything in between. They’re the closest thing to a real user and the most expensive tests you’ll write. Integration tests are narrower. They test a specific seam, not the whole journey. A checkout E2E test would log in, add an item, enter payment details, and confirm the order. The equivalent integration tests would separately verify that the cart service talks to inventory, that the order service talks to payment, and that the confirmation event reaches the email service.
What Is System Integration Testing (SIT)
System integration testing is integration testing at the level of whole systems rather than modules. Instead of checking that two classes work together, SIT checks that your application works with the other systems it depends on: an ERP, a payment processor, a partner’s API, and a data warehouse.
SIT typically happens after each system has passed its own internal testing and before user acceptance testing. It’s common in enterprise environments where multiple vendors or teams own different pieces, and none of them can see the full picture alone.
How to Run Integration Tests: Workflow and Best Practices
A repeatable workflow keeps integration tests focused and maintainable. Here’s one that works:
1. Identify the boundary under test
The first step is to identify the boundary under test. Be specific. “Test the order service” is too broad. “Test that the order service correctly persists an order and publishes an OrderCreated event” is a valid boundary. Every integration test should be able to name the seam it’s covering.
2. Define your scenarios
For each boundary, list the happy path and the failure modes that matter. What happens when the database is unavailable, when the API returns a 500, or when the event payload is missing a required field? Failure scenarios are where integration tests earn their keep. Skipping them turns the suite into a smoke test.
3. Choose your test doubles
Decide what’s real and what’s faked. A common pattern is:
- Real: your own database (in a container), your own services
- Faked: third-party APIs, payment providers, anything with rate limits or cost
The rule of thumb is to keep real anything you own and control, and fake anything you don’t. Fakes for external services should be based on recorded real responses, not guesses.
3. Implement using Arrange-Act-Assert-Teardown
To implement, arrange the state (seed the database, start the container, configure the mock), act by calling the code under test, assert on the outcome, including side effects like database rows or published events, and tear down so the next test starts clean. Teardown is the step teams skip, and it’s the source of most flaky integration suites. If test A leaves a row behind and test B assumes an empty table, you get failures that depend on execution order.
4. Run in CI
Integration tests belong in the pipeline, not on someone’s laptop. Run them on every pull request if they’re fast enough, or on merge to main if they’re not. Spin up fresh infrastructure for each run rather than sharing a long-lived test database.
5. Triage and report
An integration failure needs to answer three questions fast: which boundary, which scenario, and what changed. Structured test names and clear assertion messages help. So does tracking results over time, so you can distinguish a new regression from a flaky test that's been flaky for weeks and nobody has fixed.
What Is Automated Integration Testing
Automated integration testing means the tests run without human intervention as part of your build. The code, the infrastructure setup, the execution, and the reporting are all scripted. The alternative, manual integration testing, still has a place for exploratory work and for one-off SIT cycles. But for anything that needs to run repeatedly, automation is the only option that scales.
Automated integration testing, while necessary for repetitive runs, fails in predictable ways, sich as:
- Overly broad test scope. A test that exercises five services at once is an E2E test in disguise. When it fails, you’re back to guessing.
- Shared mutable state between tests. Tests that read or write the same rows, files, or queues without isolation will pass alone and fail together.
- Hard-coded timeouts that pass locally and fail in CI. Your laptop starts a container in two seconds. The CI runner takes eight. Poll for readiness instead of sleeping for a fixed duration.
- Brittle assertions on non-deterministic data. Timestamps, auto-generated IDs, and ordering of unordered collections will vary. Assert on what matters, not on exact output.
Integration Testing Tools Worth Knowing in 2026
Integration testing doesn’t have a single dominant tool. Here’s what your stack needs to contain: You assemble a stack: a test harness to run tests, infrastructure tooling to provide real dependencies, mocking tools for external services, contract testing for API boundaries, and a test management layer to keep track of it all.
Test Harnesses
A test harness is necessary to run tests. Good tool options are:
- JUnit 5. The default for JVM projects. Its extension model supports lifecycle hooks for starting and stopping infrastructure around test classes.
- pytest. The standard for Python. Fixtures with configurable scope make it straightforward to share a database connection across a test module and tear it down afterward.
- Jest. Widely used for JavaScript and TypeScript. Works for integration tests against Node services, though you’ll typically pair it with a separate tool for infrastructure.
Infrastructure & Mocking
Infrastructure and mocking help provide real dependencies.
- Testcontainers. Spins up real databases, message brokers, and other services in Docker containers from within your test code, then tears them down. Available for Java, Go, .NET, Python, Node, and others. It’s become the standard answer to “how do I test against a real Postgres without a shared test database?”
- WireMock / Hoverfly. Both simulate HTTP APIs. WireMock lets you define stubbed endpoints with request matching and response templating. Hoverfly can capture real traffic and replay it, which is useful for building fakes of third-party APIs from actual responses.
- Postman / Newman. Postman collections define API requests and assertions. Newman runs those collections from the command line, so the same checks you built interactively can run in CI.
Contract Testing
Contract testing is a specialized form of integration testing where the consumer and provider each verify against a shared contract rather than testing against each other directly. It’s particularly useful for microservices owned by different teams.
- Pact. Good for consumer-driven contract testing. The consumer defines what it expects, generates a contract file, and the provider verifies it can meet that contract. Supports multiple languages through a shared core.
- Spring Cloud Contract. Good for contract testing for JVM services in the Spring ecosystem. Contracts are written in Groovy or YAML and generate both provider-side tests and consumer-side stubs.
Test Management
A test management layer is necessary to keep track of everything you will do in the steps above.
TestFiesta. TestFiesta is a test management platform that helps track which integration scenarios exist, which boundaries are covered, what’s passing over time, and which failures have already been filed as bugs. In addition to supporting your integration testing, it can cover all the test cases in your entire testing pyramid, helping you build reliable, scalable products.
FAQs
Is integration testing the same as API testing?
No, integration testing is not the same as API testing, though they overlap. API testing verifies that an API behaves according to its specification. Integration testing verifies that components work together, and one of those components might be an API.
Can integration tests replace end-to-end tests entirely?
Not entirely. Integration tests verify that each boundary works. E2E tests verify that the whole journey works, including the frontend and things like session handling that integration tests don’t touch. A good suite has many integration tests and a small number of E2E tests covering the critical paths. Eliminating E2E completely means something important will remain untested.
How many integration tests should a healthy test suite have?
There’s no fixed number for integration tests in a suite. The testing pyramid suggests that integration tests should be fewer than unit tests and more than E2E tests, but the right count depends on how many boundaries your system has and how risky each one is.
What’s the difference between integration testing and UAT?
Integration testing checks that the system’s components work together technically. User acceptance testing (UAT) checks that the system does what the business needs, usually performed by end users or business stakeholders against realistic scenarios.



_%20All%20Phases%20Explained%20-%20Main%20Image.png)
.png)
