What Is Integration Testing? Types, Examples & Best Practices
Integration testing checks that different parts of a software system work properly together. Each part might work fine alone and still fail when connected. Integration tests catch those failures before your users do.
Keytake aways
It tests the connections between parts, like an app and its database, or your system and a payment provider.
It sits between unit testing (one small piece) and end-to-end testing (the whole journey).
Most bugs it finds show up when things go wrong, so test failures, not just success.
What is integration testing?
Think of building a car. The engine runs perfectly on a test bench. The steering works perfectly too. But until you put them in the same car, you don't know whether they fit and work together.
Software is the same. Integration testing is the point where you connect the parts and check that they cooperate.
Why unit tests aren't enough
A unit test checks one piece in isolation, so it can't see a mismatch between two pieces. For example, one team changes a price field from dollars to cents. Their own tests pass. The other team's tests also pass, because they never talk to the real service. But once the two are connected, customers get charged the wrong amount.
Neither part is "broken." The problem is how they talk to each other, and that's what integration testing looks for.
A simple example: online checkout
Say a shop's checkout works like this. The order system saves the order, asks the inventory system to reserve the item, asks a payment provider to charge the card, and then sends a confirmation email.
Every one of those links is a place where things can go wrong. An integration test might check:
A successful order saves correctly, reduces stock, and charges the right amount.
A declined card puts the stock back and doesn't send a confirmation.
A slow payment provider doesn't leave the customer waiting forever.
Clicking "Pay" twice charges the card once, not twice.
Notice that most of these are about problems, not the happy path.
Types of integration testing
These are four ways to decide in what order to connect and test the parts:
Big bang: connect everything, then test it all at once. It's simple, but when something breaks it's hard to see why. Best for small systems.
Top-down: start with the main, high-level parts and work downward, using temporary stand-ins ("stubs") for parts that aren't built yet.
Bottom-up: start with the low-level building blocks and work upward, using temporary callers ("drivers") for the parts above them.
Hybrid (sandwich): do top-down and bottom-up at the same time and meet in the middle. It suits large teams working in parallel.
In practice, most teams simply add one connection at a time and test each as they go. That's the safest default.
Narrow vs broad integration tests
There's another way to split things that matter more day to day. A narrow test checks one connection, often using a stand-in for the outside service. A broad test needs every real part running. Fowler points out that narrow tests often run fast enough to catch problems early in a build pipeline. Broad tests are slower, costlier, and more fragile, so most teams keep only a few.
What to check where two parts connect
At every connection, work through these questions:
Data sent and received: are the field names, formats, and units what the other side expects?
Data saved: after the call, is the right thing stored on both sides?
Login and permissions: what happens with an expired, missing, or wrong credential?
Errors: what happens when the other side returns an error or garbage?
Slowness: does the system give up after a sensible time instead of hanging?
Repeats: if the same request arrives twice, does it cause the effect twice?
Knock-on effects: do the emails, events, and logs that should follow actually happen, once?
How to do integration testing
List the connections. Write down every place two parts talk to each other.
Pick the risky ones first. Payments and customer data matter more than a newsletter signup.
Set up a safe test environment. Make it close to the real one, but use fake data, never real customer data.
Write test cases. Use the checklist above for each connection, covering success and failure.
Run and fix. Add one connection at a time so you can see what broke.
Automate what repeats. Run the quick tests every time code changes, and the bigger ones less often.
Real services or fakes?
You don't have to choose one or the other. Use each where it makes sense:
Use the real thing for parts you own and where real behaviour matters, like your own database.
Use a fake (a "stub") when you need predictable results, such as a declined card or a timeout. You can't make a real provider fail on demand.
Keep a few tests against the real outside service (or its test sandbox), to make sure your fake still matches reality.
A fake only reflects what you assume the other side does. If the assumption is wrong, your tests pass and real life fails. Contract tests help here: each side writes down what it expects from the other, and both are checked against that agreement.
Integration testing for APIs, microservices and event-driven systems
Modern systems add a few extras to watch for:
APIs and databases: send a request, then check both the response and what was saved. Include bad input and duplicates.
Microservices: many small services owned by different teams are easy to break by changing an interface. Contract tests catch this early.
Event-driven (message queues): results may arrive late, so wait for a condition rather than a fixed delay. Also test that a message delivered twice doesn't cause the effect twice.
Common problems and quick fixes
Tests pass sometimes and fail sometimes (flaky): usually caused by fixed waits or shared data. Wait for a condition instead, and give each test its own data. Don't just re-run until green.
Works on your machine, fails in the pipeline: the environments differ. Set them up the same way every time.
Tests break after a partner changes something: your fake is out of date. Add contract tests and refresh it.
The suite is too slow: too many broad tests. Move checks into narrower, faster ones.
You can't tell where it failed: log each request and response, and tag them with an ID so you can follow one order across systems.
Integration testing vs unit and end-to-end testing
Integration testing is one of the software testing methods used to verify how different parts of an application work together.
What it checks | Speed | |
Unit test | One small piece on its own | Fastest |
Integration test | Whether connected parts work together | Medium |
End-to-end test | A full user journey, start to finish | Slowest |
A good test suite has many unit tests, a moderate number of integration tests, and a few end-to-end tests.
Don't confuse integration testing with system integration testing. That's a larger check that whole systems from different vendors, like a CRM and a billing platform, work together.
Integration testing tools
Pick tools by the job they do:
Writing and running tests: JUnit, pytest, NUnit, Jest
Testing APIs: Postman, REST Assured
Contract testing: Pact
Faking outside services: WireMock, MockServer
Running real databases in disposable containers: Testcontainers
Running tests automatically: Jenkins, GitLab CI, GitHub Actions
Most teams use just two or three of these. Choose based on the language and tools your team already uses.
Best practices
Start with the connections that would hurt most if they broke.
Keep each test independent, so tests can run in any order.
Test failures and edge cases, not only the happy path.
Treat flaky tests as bugs and fix them, rather than ignoring a red build.
Keep the suite small and focused, since slow tests get skipped.
Re-check your tests whenever an integration changes.
FAQs
Is integration testing white box or black box?
It can be either, and is often a mix. You might call an API from the outside but also check the database afterward.
Who does integration testing?
Often developers, sometimes QA engineers, and often both. It depends on the team.
What's the difference between integration testing and API testing?
API testing means sending requests and checking responses. It counts as integration testing when the API runs with its real connected parts, like a database.
Should integration tests be manual or automated?
Try things manually when you're exploring something new. Automate anything you'll need to repeat.
When should you run integration tests?
Run the quick ones on every code change, and the bigger ones before a release or on a schedule.
