Integration Testing
Integration testing is the test level that focuses on the interactions between components or between systems. Each part may already pass its own tests; integration testing asks whether they work together: whether the data one part sends is what the other expects, in the format, units, order and timing it expects. Example: a checkout sends "amount": 150.00, meaning 150 riyals, to a payment gateway that reads amounts in halalas, and the customer is charged 1.50 SAR. The defect is in neither component but in the interface between them.
The ISTQB Foundation syllabus v4.0 distinguishes two integration levels, component integration and system integration, described below. Both build on component testing: parts proven in isolation are now combined.
Why it matters
An interface is where two people’s assumptions meet. When their readings of the agreement differ (riyals or halalas, local time or UTC, a missing field or a null), every component test passes and the defect appears only when the parts run together. A stub rarely reveals it, because it is usually written with the same assumption as the code that calls it.
For the product, integration defects mean wrong charges, lost orders and duplicate messages, often found in production because an external system was only ever simulated. For the tester, this is where reading both sides of an interface pays off.
Component integration and system integration
Integration tests concentrate on the interface: the calls, messages and data passed between parts, and what happens when the other side fails, not the logic inside each component. The test basis is whatever describes the interface: an API specification such as an OpenAPI document, message formats, sequence diagrams.
- Component integration testing: the interfaces between the components of one system, such as the order service calling the inventory component. Usually automated by developers in the continuous integration pipeline.
- System integration testing: the interfaces with other systems and external services, such as a payment gateway or an SMS provider. Usually led by testers; it needs an environment close to production, a sandbox from the other party, and coordination with a team you do not control.
Big-bang or incremental: top-down, bottom-up, stubs and drivers
In big-bang integration, all components are combined at once and tested together. No stand-ins are needed, but testing starts only when every part is ready, and when a test fails the cause can be in any interface. That is acceptable for a very small system; in a larger one, finding the failing interface can take longer than the testing.
Incremental integration adds one component, or a few, at a time and tests each new interface before the next, so a failure points first to the interface just added. Stand-ins, which you met in component testing, replace the missing parts. A stub replaces a called component and returns prepared answers. A driver replaces a calling component: a test script or an API client that calls the component under test as the missing caller would.
- Top-down: start at the top (an API endpoint or the user interface) with stubs for the components below, and replace them one by one. The main flow is visible early; calculations and data access are tested last, and stubs that must return realistic data can grow complex.
- Bottom-up: start with the lowest components (calculations, data access, clients for external services), called by drivers, and work upwards. Foundations are tested early and failures are easy to locate; the complete flow is visible only at the end.
- Many teams combine both and meet in the middle, which is called sandwich or hybrid integration.
Big-bang
- 1Every component built and component-tested
- 2All combined in one stepFrom
checkout-apidown topayment-client - 3A test failsThe cause can be in any interfaceFail
- Stand-ins needed:
- None
- Testing starts:
- When every part is ready
- A failure points to:
- Any of the interfaces
- Seen early · tested late:
- Nothing early; everything at the end
Top-downincremental
- 1Start with
checkout-apiA stub replacesorder-service - 2Real
order-servicereplaces its stubStubs for the components below it - 3Real lower components replace the last stubs
- Stand-ins needed:
- Stubs, in place of called components
- Testing starts:
- As soon as the top layer exists
- A failure points to:
- First the interface just added
- Seen early · tested late:
- Early: the main flow · Late: calculations and data access
Bottom-upincremental
- 1Drivers call the lowest components
price-calculator,inventory,payment-client - 2A driver calls
order-serviceNow with the real lower components - 3
checkout-apireplaces the driver
- Stand-ins needed:
- Drivers, in place of the callers
- Testing starts:
- As soon as the lowest components exist
- A failure points to:
- First the interface just added
- Seen early · tested late:
- Early: the foundations · Late: the complete flow
Try it: Mock API Response Generator Try it: API Request Builder
Interface defects: what to look for
Interface defects come from two sides that disagree, or from one side that cannot cope when the other fails. A practical first check is to compare each side’s actual requests and responses with the agreed specification, such as the OpenAPI document: a difference shows which side departs from the agreement. Then ask these questions of every interface:
- Format and encoding: is
01/02/2026the 1st of February or the 2nd of January? Do Arabic names arrive intact? - Units and meaning: riyals or halalas, seconds or milliseconds, local time or UTC, price with or without VAT.
- Structure: a missing, renamed or extra field;
nullversus an absent field; enum values that differ (CANCELLED,CANCELED); different length limits on each side. - Order and timing: calls in the wrong order, a callback that arrives before its record is saved, a timeout shorter than the other side’s normal response time.
- Failure handling: when the other side is down, slow, returns a 500 or an unexpected error body, or answers twice, does the caller retry safely, avoid charging twice and keep the data consistent?
Try it: API Contract Validator Try it: Response Contract Comparator
Worked example: integrating a checkout
A shop’s checkout has five components and calls one external system, as in the plan below. The team integrates bottom-up: the checkout API is written last, and the gateway’s sandbox account is still being arranged, so a stub stands in for the gateway in steps 1 to 3.
The stub was written from the payment client’s own code, so it accepted any amount, and every test in steps 1 to 3 passed. Step 4 runs the same tests against the real sandbox:
- A 150.00 SAR order creates a 1.50 SAR charge: the gateway expects whole halalas, and in JSON
150.00is simply the number 150. The defect report names the interface (payment-client to gateway), the request and the response. - Step 4 also covers what only the real system shows: its actual decline codes, its behaviour on timeouts and retries (one charge only), and when its callback arrives.
checkout-api POST /checkout (top)
└── order-service placeOrder(cart)
├── price-calculator total(cart): SAR, 2 decimals
├── inventory reserve(sku, qty), release(sku, qty)
└── payment-client charge(amount, currency)
└──► payment gateway (external system)
Integration order: bottom-up
1 drivers → price-calculator, inventory, payment-client gateway: stub
2 driver → order-service + the components from step 1 gateway: stub
3 checkout-api replaces the driver gateway: stub
4 the same tests, against the gateway sandbox system integrationTry it: API Status Code Simulator Try it: Mock Error Response Generator
Common mistakes
- Re-testing each component’s business rules instead of the interface: the data passed, the order of calls, the handling of failures.
- Trusting a stub built from the caller’s own assumptions instead of the agreed specification, and never testing against the real system.
- Integrating a large system big-bang, then spending days finding which interface failed.
- Testing only the happy path: never the timeout, the rejected request, the error body or the duplicate callback.
- Requesting external sandbox access too late, which squeezes system integration into the last days before release.
- Assuming "integration test" means the same to everyone. Ask which parts are real and which are replaced.
The QA perspective
A tester’s integration work starts when interfaces are designed. Read the API specification and sequence diagrams during refinement, and ask both sides the same questions: units and formats, required fields, error codes, timeouts, retries. A disagreement settled then never becomes a defect.
Developers usually automate component integration tests; review which interfaces and error paths they cover. Testers usually lead system integration: arrange sandbox access and test data with the external party early, and learn where the sandbox differs from production. When an integration test fails, the report names the interface, includes the exact request and response, and states which side departs from the specification.
Practice exercise
Plan the integration testing of an appointment reminder
A clinic app sends an SMS reminder 24 hours before each appointment; its parts are below. The booking API is still being written, and the SMS provider’s sandbox will be ready in two weeks. Plan the integration testing.
booking-api POST /appointments (top)
└── appointment-service book(patient, slot), cancel(id), sendDueReminders(now)
├── schedule-repository save(appointment), findDue(now)
└── sms-client send(phone, text, sendAt)
└──► SMS provider POST /v1/messages (external system)
Clinic data: phone numbers as typed by staff (e.g. 05XXXXXXXX),
appointment times in Riyadh time (UTC+3)
send(phone, text, sendAt)
phone international format (E.164), e.g. +9665XXXXXXXX
text reminder text, Arabic or English
sendAt ISO 8601 date-time, UTC
SMS provider responses
202 {"id": "msg-123", "status": "queued"}
400 invalid number 429 too many requests 503 unavailable- Mark each interface as component or system integration.
- Choose an integration order and list the stubs and drivers it needs.
- Write five integration test conditions for the reminder path, each naming the interface defect it targets.
- List what must still be checked against the real provider.
Show the model answer
Component integration: booking-api → appointment-service, appointment-service → schedule-repository, appointment-service → sms-client. System integration: sms-client → SMS provider.
Bottom-up, because the top layer is not ready: integrate schedule-repository and sms-client, then appointment-service. A driver (a test script or an API client) calls book and cancel until booking-api replaces it, and calls sendDueReminders with a chosen now, which makes the time conditions repeatable. A stub plays the SMS provider and returns 202, 400, 429, 503 and a slow answer.
Conditions: (1) an appointment at 10:00 Riyadh time gives sendAt 07:00 UTC the day before (time zone); (2) 05XXXXXXXX is sent as +9665XXXXXXXX, never prefixed twice (format); (3) an Arabic reminder arrives readable (encoding); (4) a 503 or a timeout keeps the appointment and retries later, sending one SMS only (failure handling); (5) cancelling before sendAt means no reminder (order and timing).
Against the real provider: that its responses match what the stub returned (status codes and body fields), that it reads sendAt as UTC, the sender name, the actual rate limit, how message length is counted for Arabic text, and whether the sandbox returns errors as production does.
Frequently asked questions
Is integration testing the same as end-to-end testing?
No. Integration testing targets specific interfaces: two components, or your system and another one. An end-to-end test follows a complete user journey through the whole system and crosses many interfaces at once. It can reveal integration defects, but it is slower and makes the failing interface harder to find. System and end-to-end testing come in the next lessons.
Who does integration testing, developers or testers?
Usually developers automate component integration tests and testers lead system integration testing, often together with the external party. Teams differ, so agree on it explicitly; either way, a tester reviews which interfaces are covered and whether error paths are tested.
Developers call a test with a real database an integration test. Is that correct?
Many teams use the term that way: any automated test that goes beyond one unit, such as a service with its real database or a real HTTP call. It fits the broad definition, interactions between parts. When someone says "integration test", ask which parts are real and which are replaced; that tells you what the test can and cannot find.
How does continuous integration relate to integration testing?
Continuous integration is a development practice: developers merge small changes into the main line often, and each merge is built and tested automatically. It keeps every integration step small, and integration tests usually run in that pipeline. It does not replace system integration testing with external systems, which needs its own environment.
References
- ISTQB Certified Tester Foundation Level (CTFL) v4.0The foundation syllabus of software testing: test levels and types, test design techniques, test management and tools.
- ISTQB GlossaryThe standard definitions of testing terms — test case, regression testing, severity, priority, boundary value analysis and the rest.
- The Practical Test Pyramid (Ham Vocke)A widely cited explanation of how to balance unit, integration, contract and UI tests in an automated test portfolio.
- Continuous Integration (Martin Fowler)The widely referenced description of continuous integration: integrate often, keep the build self-testing and fast, and fix a broken build immediately.
Practise with Mutqan tools
Generate complete mock API responses (status, headers, JSON body) for success, list, detail, error and empty cases.
Get a public endpoint that returns any HTTP status code, with optional delay and custom JSON body, to test client error handling.
Generate structured error responses (validation, auth, not found, rate limit, server error) in popular formats.
Validate that an API request/response pair conforms to its OpenAPI contract: path, method, parameters, body and response schema.
Compare an actual API response against an expected contract (example or schema) and report breaking vs non-breaking differences.
Compose and send HTTP requests (method, URL, headers, auth, body) and inspect the response — a lightweight Postman in your browser.
Go deeper
- End-to-End Product Quality AssuranceTest levels: unit, integration, system and acceptance
- API Testing Approaches ComparedMocking: testing the consumer and the error paths
- API Testing Approaches ComparedContract testing: the specification as the oracle
- CI/CD TestingTest stages in a pipeline and why the order matters
Your progress on this topic
Not started
Mark the topic complete when you have studied it and done the exercise.
Sign in to track your progress
Mark topics complete, continue where you left off and unlock specialisation exams.