Module 4 Session 4.2 Integration Testing

Integration Testing Approaches

Contrast top-down, bottom-up, and hybrid integration strategies — ISTQB Foundation Level §4.2 & §4.3

Learning Objectives

LO 4.2.1 — Define integration testing and distinguish component integration testing from system integration testing.
LO 4.2.2 — Explain the top-down integration strategy: its sequence, required test doubles (stubs), advantages, and limitations.
LO 4.2.3 — Explain the bottom-up integration strategy: its sequence, required test doubles (drivers), advantages, and limitations.
LO 4.2.4 — Describe hybrid (sandwich) integration and identify when it is preferable to pure top-down or bottom-up.
LO 4.2.5 — Identify the risks of big-bang integration and explain why incremental approaches are preferred.
LO 4.2.6 — Select the most appropriate integration strategy given a system architecture description.
LO 4.2.7 — List typical interface defects discovered during integration testing.

Concept Overview

Integration testing verifies the interfaces and interactions between integrated components or systems. Where unit testing asks "does this function work?", integration testing asks "do these components work together correctly?"

ISTQB FL v4.0 §4.2: "Integration testing focuses on interactions between components or systems. Defects are often found in the interfaces, data passed between components, integration sequences, and third-party or legacy system interactions."

What Is Integration Testing?

Key Definitions
Interface
The shared boundary between two components where data, control, or events are exchanged (API calls, method parameters, shared files, message queues, databases).
Integration
The process of combining individually tested units into larger subsystems and eventually a complete system.
Integration Testing
Testing to expose defects in the interfaces and interactions of integrated components or systems.
Integration Strategy
The plan specifying the order in which components are combined and tested — top-down, bottom-up, hybrid, or big-bang.
Why Integration Defects Are Hard to Find
Emergent behaviour — A defect exists only when two correct components interact; neither unit test detects it in isolation.
Data format mismatches — Component A sends a date as "YYYY-MM-DD"; Component B expects "DD/MM/YYYY". Both unit tests pass; integration fails.
Timing and concurrency — Race conditions only appear when two threads interact, invisible in single-threaded unit tests.
Third-party contracts — An external API changes its response schema; the internal unit tests still pass against mocked responses.

Component Integration vs System Integration Testing

AttributeComponent Integration Testing (CIT)System Integration Testing (SIT)
ScopeInteractions between units/components within one subsystemInteractions between entire subsystems or external systems
V-model levelCorresponds to component designCorresponds to system architecture / HLD
BasisComponent interface specifications, class diagramsSystem architecture, interface control documents, API contracts
Typical interfacesFunction calls, shared memory, object method callsREST/SOAP APIs, message brokers (Kafka/RabbitMQ), databases, file systems
EnvironmentDeveloper workstation or CI pipeline; lightweight stubs/mocksIntegration or staging server; real (or emulated) external systems
Who tests?Developer or SDETQA engineer with system architecture knowledge
Typical defect typesWrong parameter types, missing return values, incorrect object state after callAuthentication failures, serialisation errors, SLA violations, missing error handling across system boundaries

Top-Down Integration Strategy

Definition: Begin with the highest-level (controlling) module and progressively integrate lower-level modules. Lower-level components that are not yet ready are replaced by stubs.

Direction: Root → leaves of the call hierarchy (breadth-first or depth-first).

Architecture Example — 3-Layer Web Application
┌──────────────────────────────────┐ │ PRESENTATION LAYER (UI/API) │ ← Step 1: Test first (real) │ UserController │ └────────────┬─────────────────────┘ │ calls ┌────────────▼─────────────────────┐ │ SERVICE LAYER │ ← Step 2: Replace stub; test real │ UserService │ └────────────┬─────────────────────┘ │ calls ┌────────────▼─────────────────────┐ │ DATA ACCESS LAYER │ ← Step 3: Replace stub; test real │ UserRepository │ └──────────────────────────────────┘ Integration sequence: Step 1: UserController + STUB(UserService) + STUB(UserRepository) Step 2: UserController + UserService(real) + STUB(UserRepository) Step 3: UserController + UserService(real) + UserRepository(real)
Top-Down: Advantages vs Disadvantages
Early UI/workflow validation — High-level control flow is testable immediately, even before low-level modules are coded.
Early defect detection in architecture — Structural flaws in the control flow surface early.
Progressive integration — Only one new module is added per integration step, limiting defect localisation effort.
Demos to stakeholders — A working (stubbed) UI can be shown to stakeholders early.
Many stubs needed — Writing and maintaining realistic stubs is expensive; poorly written stubs give false confidence.
Low-level modules tested late — Critical algorithms and data logic (often highest risk) are exercised last.
Stub accuracy problem — A stub that doesn't faithfully reproduce real behaviour hides real integration defects.
Parallel development is hard — Low-level developers can't verify their code until upper layers are integrated.
Stub Design Guidelines

A stub must faithfully simulate the interface contract of the real module it replaces. Stubs should:

  • Accept the correct parameter types and validate inputs (don't silently accept wrong types).
  • Return realistic data structures, not just None or hardcoded empty objects.
  • Simulate different response scenarios (success, empty result, error) using configurable mode switches.
  • Be documented and version-controlled alongside the tests that use them.

Bottom-Up Integration Strategy

Definition: Begin with the lowest-level (leaf) modules and progressively integrate higher-level modules. Higher-level components not yet available are replaced by drivers.

Direction: Leaves → root of the call hierarchy.

Architecture Example — Same 3-Layer Application
┌──────────────────────────────────┐ │ PRESENTATION LAYER (UI/API) │ ← Step 3: Replace driver; test real │ UserController │ └────────────┬─────────────────────┘ │ ┌────────────▼─────────────────────┐ │ SERVICE LAYER │ ← Step 2: Replace driver; test real │ UserService │ └────────────┬─────────────────────┘ │ ┌────────────▼─────────────────────┐ │ DATA ACCESS LAYER │ ← Step 1: Test first (real) │ UserRepository │ └──────────────────────────────────┘ Integration sequence: Step 1: DRIVER(UserService calls) + UserRepository(real) Step 2: DRIVER(UserController calls) + UserService(real) + UserRepository(real) Step 3: UserController(real) + UserService(real) + UserRepository(real)
Bottom-Up: Advantages vs Disadvantages
Critical modules tested first — Business logic and data layers (highest technical risk) are verified before upper layers depend on them.
Drivers simpler than stubs — A driver just calls the module; it doesn't need to simulate complex behaviour like a stub does.
Parallel development supported — Low-level teams can test independently using drivers while upper-layer development continues.
Better for reusable components — Libraries and utilities are tested thoroughly before anything depends on them.
UI/workflow tested last — End-to-end control flow is not visible until all lower-level integration is complete.
High-level design flaws found late — Architectural problems in the control flow surface only in the final integration steps.
No early demo capability — Stakeholders cannot see working end-to-end behaviour until late in the cycle.
Many drivers needed — Each integration step needs a new driver; driver code is test-only and discarded later.
Driver vs Stub — Side-by-Side
PropertyStub (used in Top-Down)Driver (used in Bottom-Up)
ReplacesA lower-level module not yet availableA higher-level module not yet available
RoleCalled by the unit under test; returns canned dataCalls the unit under test; simulates the caller
ComplexityHigher — must faithfully simulate return values and stateLower — just invokes the UUT with test inputs
LifespanReplaced as real lower-level modules are integratedReplaced as real higher-level modules are integrated
Code exampleStub returns {"user": "Alice", "role": "admin"} instead of calling DBDriver calls UserRepository.findById(42) and prints the result

Hybrid (Sandwich) Integration Strategy

Definition: Combines top-down and bottom-up simultaneously. The system is divided into three tiers:

  • Top tier — integrated top-down (UI/API controllers).
  • Middle tier (target layer) — the integration meeting point, often the service/business logic layer.
  • Bottom tier — integrated bottom-up (repositories, utilities, external adapters).
TOP-DOWN ↓ (stubs replace middle layer) BOTTOM-UP ↑ (drivers replace middle layer) ┌──────────────────────┐ ┌──────────────────────┐ │ UserController │ │ UserRepository │ │ OrderController │ ← real, tested top-down │ PaymentGateway │ ← real, tested bottom-up └──────────┬───────────┘ └──────────┬───────────┘ │ stubs ↓ │ drivers ↑ ┌──────────▼────────────────────────────────────────────────▼───────────┐ │ SERVICE LAYER (Target Layer) │ │ UserService OrderService PaymentService │ │ ← Integrated last; real calls replace stubs & drivers → │ └────────────────────────────────────────────────────────────────────────┘
Hybrid Advantages
Both UI and critical back-end modules are tested early in parallel.
Fewer stubs and drivers overall compared to pure strategies — only the middle layer needs both.
Supports parallel development across all tiers simultaneously.
Early visibility at both the interface and business logic levels.
Hybrid Disadvantages
More complex to manage — requires coordination between top-down and bottom-up teams.
The "meeting point" (target layer) must be carefully chosen; wrong choice increases rework.
Stubs and drivers are still needed — just fewer of each.
Test planning is more complex; integration order must be explicitly scheduled.

Big-Bang Integration

Definition: All components are integrated simultaneously in one step and the whole system is tested at once. No stubs or drivers are used; all units must be complete before integration begins.

Component A ──┐ Component B ──┤ Component C ──┼──► Integrate ALL at once ──► Test entire system Component D ──┤ Component E ──┘ Result: If 37 tests fail, which interface caused which failure? Debugging is extremely difficult — no intermediate states exist.
When Big-Bang Is Justified
Very small systems (2–3 components) where incremental integration adds more overhead than benefit.
Retrofitting integration tests onto existing legacy systems that were never designed for incremental integration.
Systems where all components are mature and well-specified with high confidence.
Big-Bang Risks (why it's usually rejected)
Defect localisation is very hard — A failure could originate from any of the N interfaces; no intermediate state to isolate.
Late integration — All development must complete before any integration testing; delays compound.
High rework cost — Defects found late are up to 100× more expensive to fix than those found at component level.
No early feedback — Architectural problems remain hidden until the final integration point.

Integration Strategy Comparison

Criterion Top-Down Bottom-Up Hybrid Big-Bang
Test doubles needed Many stubs Many drivers Some stubs + some drivers None
Early UI/control-flow test Yes No Yes No
Critical module tested early No Yes Yes No
Parallel development Limited Good Best None
Defect localisation Good (one new module per step) Good (one new module per step) Good (coordinated steps) Very poor
Stub/driver effort High Medium Medium None
Stakeholder demo capability Early Late Early Late
Scheduling complexity Low Low High Lowest (but risky)
Best suited for UI-critical, monolithic systems Algorithm-heavy, reusable-library systems Large multi-team enterprise systems Tiny systems; legacy retrofit
ISTQB recommendation Acceptable Acceptable Preferred for large systems Discouraged
Strategy Selection Guide
Is the system small (≤ 3 components)? YES → Big-Bang is acceptable NO → Use incremental integration ↓ Is the UI / control flow the highest risk? YES → Top-Down NO → ↓ Are critical algorithms / data layers the highest risk? YES → Bottom-Up NO → ↓ Is the system large with multiple parallel teams? YES → Hybrid (Sandwich) NO → Choose based on team skill and stub/driver effort

Typical Defects Found in Integration Testing

Defect CategoryDescriptionExampleDetected by
Data format mismatch Component A passes data in a format Component B does not expect A sends date as Unix timestamp; B expects ISO 8601 string Any integration strategy
Incorrect parameter passing Wrong number of arguments, wrong order, or wrong data types across the interface Function called with (amount, accountId) but signature expects (accountId, amount) Any integration strategy
Missing error propagation A lower-level exception is swallowed or incorrectly translated at a higher layer DB constraint violation caught but not surfaced to the UI, returning HTTP 200 on failure Top-down (after real module integrated)
State corruption Component A leaves shared state in an inconsistent condition that corrupts B's subsequent operations Session object mutated by auth module breaks the order processing module Hybrid; any strategy with shared state
Timing / race condition A concurrency defect in the interaction between two components Inventory decrement and payment processing run in parallel; oversell occurs System integration testing
Protocol mismatch Components use incompatible communication protocols or versions Service A expects OAuth 2.0 Bearer tokens; Service B sends Basic Auth System integration testing
Missing integration logic A required step in the interaction is omitted (e.g., commit, flush, close) Transaction started but never committed; data silently discarded Bottom-up / hybrid
Non-functional interface violations Response time, throughput, or payload size exceeds agreed limits Internal API returns 2 MB JSON payload where the consumer expects ≤ 100 KB System integration testing with performance tools

Worked Example: E-Commerce Order System

Given the following system architecture, select and justify an integration strategy, then derive the integration sequence.

System Architecture
┌─────────────────────────────────────────────────────┐ │ PRESENTATION LAYER │ │ OrderController CartController UserController │ └───────────────────────┬─────────────────────────────┘ │ ┌───────────────────────▼─────────────────────────────┐ │ SERVICE LAYER │ │ OrderService InventoryService PaymentService │ └────────┬─────────────────┬───────────────┬──────────┘ │ │ │ ┌────────▼────┐ ┌────────▼────┐ ┌──────▼──────────┐ │ Order │ │ Inventory │ │ Payment │ │ Repository│ │ Repository │ │ Gateway Adapter│ └─────────────┘ └─────────────┘ └─────────────────┘ │ │ │ [SQL DB] [SQL DB] [Stripe API]
Strategy Recommendation: Hybrid

Justification:

  • The Payment Gateway Adapter interfaces with an external system (Stripe) — high technical risk → test bottom-up first.
  • The UI/OrderController has business-critical workflow that stakeholders need to demo early → test top-down.
  • The Service Layer is the natural "meeting point" where both directions converge.
  • Multiple teams can work in parallel: frontend team tests top-down; backend team tests bottom-up.
Hybrid Integration Sequence
StepDirectionComponents Added (Real)Test Doubles UsedObjective
1a Bottom-up OrderRepository, InventoryRepository Drivers for OrderService, InventoryService Verify DB read/write correctness
1b Bottom-up PaymentGatewayAdapter Driver for PaymentService; Stripe sandbox Verify external API integration (tokenisation, charge, refund)
2a Top-down OrderController Stub for OrderService Verify HTTP routing, request parsing, response formatting
2b Top-down CartController, UserController Stubs for respective services Verify UI workflows end-to-end (with stubs)
3 Convergence OrderService, InventoryService, PaymentService None — stubs and drivers replaced Full integration: real controllers + real services + real repositories
4 System All subsystems including external (Stripe, DB) None End-to-end order placement, payment, inventory deduction
Sample Integration Test (Python / pytest)
# test_order_service_integration.py # Step 3: OrderService integrated with real OrderRepository (SQLite in-memory) import pytest from database import create_test_db from repositories import OrderRepository from services import OrderService from models import Order class TestOrderServiceIntegration: def setup_method(self): # Arrange: real in-memory DB + real repository + real service self.db = create_test_db() # SQLite :memory: self.repo = OrderRepository(self.db) self.service = OrderService(self.repo) # no stub — real repo def test_place_order_persists_to_database(self): # Act order_id = self.service.place_order(user_id=1, product_id=42, qty=2) # Assert: verify via repository, not via service — test the interface saved_order = self.repo.find_by_id(order_id) assert saved_order is not None assert saved_order.status == "PENDING" assert saved_order.quantity == 2 def test_place_order_raises_for_zero_quantity(self): # Interface contract: service must reject qty=0 with pytest.raises(ValueError, match="quantity"): self.service.place_order(user_id=1, product_id=42, qty=0) def teardown_method(self): self.db.close()

Modern Integration Testing Patterns

In modern CI/CD and microservice architectures, classical top-down/bottom-up strategies are supplemented by new approaches.

Continuous Integration (CI) Testing
Every commit triggers the full unit + integration test suite automatically. Integration tests run in isolated Docker containers with real (but ephemeral) databases and queues. Defects are detected within minutes of introduction.
Contract Testing (Pact)
Each consumer service defines a "contract" of what it expects from a provider. The provider's CI pipeline verifies it meets all consumer contracts. Used in microservices as a lightweight alternative to full integration tests.
API Integration Testing
Tools like Postman/Newman, REST-assured, or Karate test REST/GraphQL APIs in isolation. Focuses on request/response schema, status codes, error handling, and authentication.
Service Virtualisation
Advanced form of stubbing where a virtual service records real traffic and replays it, eliminating the need to manually write stubs. Tools: WireMock, Mountebank, Hoverfly.
Consumer-Driven Contract Testing
Consumers define expected interactions; providers must satisfy all consumer contracts. Removes the need for a shared staging environment during integration.
Test Containers
Integration tests spin up real Docker containers (Postgres, Redis, Kafka) programmatically. Eliminates "works on my machine" problems; DB is fresh for each test run.
Integration Testing in CI/CD Pipeline
Developer pushes commit │ ▼ ┌─────────────────┐ │ Unit Tests │ Fast (seconds); run on every commit │ (pytest, JUnit)│ └────────┬────────┘ │ pass ▼ ┌─────────────────────────────────┐ │ Component Integration Tests │ Medium (minutes); real DB in Docker │ (TestContainers + pytest) │ └────────┬────────────────────────┘ │ pass ▼ ┌─────────────────────────────────┐ │ API / Contract Tests │ Medium; Pact / Postman │ (Pact, REST-assured) │ └────────┬────────────────────────┘ │ pass ▼ ┌─────────────────────────────────┐ │ System Integration Tests │ Slow (hours); full staging environment │ (Selenium, Karate, Gatling) │ └────────┬────────────────────────┘ │ pass ▼ Deploy to Production

Common Mistakes in Integration Testing

Skipping Integration Testing ("It worked in unit tests") — Unit tests with mocks guarantee behaviour in isolation only. Real integration exposes interface contract violations invisible to mocks.
Poor Stub Quality — A stub that always returns success masks error-handling defects. Stubs must simulate both success and failure scenarios.
Testing Integration and Business Logic Together — Integration tests should focus on the interface, not retest the same business logic already covered by unit tests. This creates slow, fragile tests.
Shared Integration Test Database — Tests that share a single database instance create ordering dependencies and data pollution. Use an isolated DB per test run (TestContainers or schema-per-test).
Ignoring Non-Functional Interfaces — Integration tests that only verify functional correctness miss timeout violations, excessive payload sizes, and SLA breaches at component boundaries.
Choosing Big-Bang for Large Systems — Schedule pressure tempts teams to skip incremental integration. This is the single most common cause of "integration hell" at the end of a project.
No Integration Test Plan — Without a defined integration sequence and entry/exit criteria, integration happens ad-hoc, making it impossible to measure progress or identify gaps.

Class Activity (20 min)

Activity: Integration Strategy Selection for a Hospital System

The hospital information system (HIS) has the following architecture. In groups of 3:

  1. Identify all component interfaces (label each interface I1, I2, …).
  2. Select an integration strategy and justify your choice (3 reasons minimum).
  3. Produce an integration sequence table (minimum 4 steps) with: components integrated, test doubles used, and objective of each step.
  4. Identify 3 typical defects you expect to find and at which integration step they would be detected.
System Architecture
┌────────────────────────────────────────────────────┐ │ HOSPITAL INFORMATION SYSTEM │ │ │ │ PatientPortal (Web UI) │ │ │ │ │ ├─── PatientService ──── PatientRepository ──┼── [MySQL DB] │ │ │ │ ├─── AppointmentService ─ AppointmentRepo ───┼── [MySQL DB] │ │ │ │ │ │ └─── NotificationService ──────────┼── [SMTP Server] │ │ │ │ └─── BillingService ──── BillingRepository ──┼── [MySQL DB] │ │ │ │ └─── InsuranceGateway ─────────────┼── [External: NHS API] └────────────────────────────────────────────────────┘
Hints
  • The InsuranceGateway calls an external NHS API — consider the risk of external dependency.
  • PatientPortal must be shown to hospital management early in the project.
  • The NotificationService sends real emails — it must be carefully isolated in testing.
Activity Rubric
CriterionExcellent (4)Good (3)Adequate (2)Needs Work (1)
Interface Identification All 7 interfaces correctly identified and labelled 5–6 interfaces identified 3–4 interfaces Fewer than 3
Strategy Selection & Justification Correct strategy with 3+ well-reasoned justifications referencing risk, team, and timeline Correct strategy, 2 justifications Plausible strategy, weak justification No clear justification
Integration Sequence 4+ steps; correct stubs/drivers; logical progression; objectives stated 4 steps; minor stub/driver errors 2–3 steps; some confusion Fewer than 2 steps
Defect Identification 3 realistic defects mapped to correct integration steps 2 defects with step mapping 2 defects, weak mapping Generic or missing defects

Exit Ticket

  1. A system has a critical payment processing module at the bottom of the call hierarchy. Which integration strategy best ensures this module is tested first, and what test double is required?
  2. What is the key difference between a stub (used in top-down) and a driver (used in bottom-up)?
  3. Give two reasons why big-bang integration is discouraged for systems with more than 5 components.
  4. Name one advantage and one disadvantage of the hybrid integration strategy compared to pure top-down.
Model Answers
  1. Bottom-up integration — integration begins with the lowest-level modules (including the payment processor). A driver is required to call the payment module and provide test inputs, replacing the not-yet-integrated higher-level service.
  2. A stub simulates a lower-level module called by the unit under test — it returns canned data. A driver simulates a higher-level module that calls the unit under test — it provides inputs and exercises the UUT. Stubs are passive responders; drivers are active callers.
  3. Any two: (a) Defect localisation is very difficult — when many tests fail it is unclear which interface caused which failure. (b) All components must be complete before any integration testing begins, eliminating early feedback. (c) Architectural defects are found very late, making rework expensive.
  4. Advantage: Both the UI and critical back-end modules can be tested early in parallel, combining the benefits of both strategies. Disadvantage: More complex to manage — requires coordination between top-down and bottom-up teams and careful selection of the target (meeting-point) layer.

Summary & Assignment

Session Summary
ConceptKey Takeaway
Integration Testing PurposeVerifies interfaces and interactions; finds defects invisible to unit tests (format mismatches, protocol errors, state corruption)
Two LevelsComponent Integration Testing (within subsystem) vs System Integration Testing (between subsystems/external systems)
Top-DownHigh-level → low-level; uses stubs; early UI test; critical modules tested late
Bottom-UpLow-level → high-level; uses drivers; critical modules tested first; UI tested late
HybridBoth directions simultaneously; converge at target layer; best for large multi-team systems
Big-BangAll at once; no stubs/drivers; very poor defect localisation; only for tiny systems
Strategy SelectionDriven by risk: where are highest-risk components? What can stakeholders see early? Team structure?
Modern PatternsCI-based integration, contract testing (Pact), service virtualisation (WireMock), TestContainers
Assignment — Lab Task

For the Hospital Information System from the class activity:

  1. Produce a formal Integration Test Plan (1–2 pages) specifying: scope, strategy chosen (with justification), integration sequence (minimum 6 steps), entry/exit criteria, test doubles needed, environment, and risk register.
  2. Implement at least 3 integration tests in pytest (or your team's agreed language) for one interface pair of your choice. Use TestContainers or an in-memory DB where applicable.
  3. Run your tests and capture the output; document any interface defects found.

Marks: 15 points — 5 for integration test plan, 7 for test implementation (correctness, interface focus, no unit-level retest), 3 for defect documentation.

Further Reading
  • ISTQB Foundation Level Syllabus v4.0 — Section 4.2: Integration Testing; Section 4.3: System Integration Testing
  • Pressman, R.S. (2014). Software Engineering: A Practitioner's Approach, 8th ed. Chapter 22: Integration Testing.
  • Richardson, C. (2018). Microservices Patterns. Chapter 9: Testing Microservices. (Contract testing, service virtualisation)
  • Pact documentation: docs.pact.io
  • TestContainers: testcontainers.com