Integration Testing
What Is Integration Testing?
Integration testing combines two or more software modules and verifies they interact correctly at their boundaries. It catches interface-level defects — data format mismatches, incorrect API contracts, authentication failures, and transaction handling errors — that unit tests cannot detect because they test modules in isolation.
In the Testing Pyramid
Integration tests sit between unit tests and end-to-end tests. Slower than unit tests because they exercise real interactions, but faster and more stable than full E2E tests because they scope to a subset of the system.
Real-World Example
A payment service that validates requests, writes to PostgreSQL, publishes to Kafka, and calls a fraud API. Unit tests mock all four. Integration tests exercise at least one real interaction.
Value Scales with Complexity
In monoliths, integration tests validate seams between modules. In microservices, they validate contracts between independently deployed services — a critical safety net for distributed systems.
Integration Testing vs Unit Testing
| Dimension | Unit Test | Integration Test |
|---|---|---|
| Scope | Single function or class | Two or more modules interacting |
| Dependencies | All mocked | Some or all real |
| Speed | Milliseconds | Seconds to minutes |
| Failure Cause | Logic errors in a single module | Interface mismatches, data format issues, network errors |
| Environment | In-process, no infrastructure | Requires databases, containers, or mock servers |
Types of Integration Testing
Each approach has distinct trade-offs in defect isolation, stub requirements, and parallelizability.
Big Bang
All modules combined at once. Simplest to implement but hardest to debug when failures occur. Best for small systems with few modules.
Top-Down
Starts from the API gateway, progressively integrating lower modules. Validates user-facing behavior early. Requires stubs for unintegrated modules.
Bottom-Up
Starts from data access layers, progressively integrating upward. Catches subtle database and serialization bugs early. Requires test drivers.
Sandwich (Hybrid)
Combines top-down and bottom-up. Best defect isolation as both high-level and low-level issues surface early. Requires stubs and drivers.
How Keploy Handles Integration Testing
Keploy eliminates environment setup overhead through its record-replay architecture powered by eBPF.
Requests
Record Phase
eBPF hooks capture all network traffic: HTTP requests/responses, database queries, downstream HTTP/gRPC calls, Redis operations, and Kafka interactions. Each interaction is stored as YAML test cases.

Replay Phase
Keploy sends recorded requests to your app and intercepts all outbound calls, returning recorded responses. Tests run without databases, services, or networks — yet exercise the full code path.

Non-Determinism Handling
AI-powered noise detection identifies varying fields (timestamps, UUIDs, sequence numbers). Time-freezing replays the system clock at the original capture time. Flaky tests eliminated.

Language Agnostic
Since Keploy operates at the kernel level, it works with Go, Java, Python, Node.js, Rust, and any other language without framework plugins or SDK dependencies.

Test Environment Strategies
Ranked from lightest to heaviest. Choose based on feedback speed requirements and fidelity needs.
In-Process Mocks
Libraries like WireMock, nock, and responses intercept HTTP calls within the test process. Fastest approach with no network I/O. Limitation: you must manually define every stub.

Traffic-Captured Mocks with Keploy
Auto-generated mocks from real traffic combine the speed of in-process mocks with real-world fidelity. No manual stub authoring required. Actual response bodies, headers, and latency characteristics.
Containerized Dependencies
Docker Compose and Testcontainers spin up real databases, message brokers, and services. Highest fidelity but adds 5-30 seconds startup time and requires Docker on CI runners.
Ephemeral Cloud Environments
On-demand Kubernetes environments with all dependencies deployed. Most realistic but slowest to provision. Reserved for pre-release validation rather than per-PR testing.

Practical recommendation: Use Keploy's traffic-captured mocks or in-process mocks for per-PR integration tests (fast feedback). Use containerized dependencies for database-specific tests that need to validate SQL migrations. Reserve ephemeral cloud environments for release candidates.
Integration Testing Best Practices
Drawn from teams operating large-scale distributed systems where test reliability directly impacts deployment velocity.
Isolate test state completely
Every test starts from a known state with no residue. Use database transactions that roll back, dedicated schemas, or containerized databases destroyed after each suite.
Scope to a single boundary
Validate one integration boundary at a time: service A to database, service A to service B. Narrow scope improves defect isolation and test stability.
Use contract tests for cross-team APIs
Use contract testing to prevent breaking changes across team boundaries. Decouples deployment schedules and removes environment coordination overhead.
Separate fast and slow tests
Run mocked integration tests on every PR for fast feedback. Run infrastructure-heavy tests on merge to main or nightly for thorough validation.
Test failure modes, not just happy paths
Test timeouts, retries, circuit breakers, and partial failures. Production incidents often come from downstream 500s, query timeouts, and queue unavailability.
Monitor integration test health
Track flake rate, mean time to fix, and execution time as team metrics. When flake rate exceeds 2%, invest in root cause fixes rather than re-running builds.
See Keploy in Action
How Keploy captures traffic, generates mocks, and eliminates flaky integration tests.
FAQs
Integration testing validates that two or more software modules or services work correctly when combined. It catches bugs at the interface layer — data format mismatches, incorrect API contracts, and failed handoffs between components — that unit tests cannot detect because they test modules in isolation.
Testcontainers spins up real Docker containers for each dependency (database, cache, queue), requiring per-language libraries, Docker-in-Docker in CI, and manual test data seeding. Keploy captures all dependency interactions from real traffic at the kernel level and replays them as auto-generated mocks — no containers, no Docker, no manual setup. This makes integration tests 10-50x faster to execute and eliminates environment parity issues.
Handwritten fixtures encode an engineer's assumptions about dependency behavior, which drift as schemas evolve and rarely cover edge cases like partial failures, connection timeouts, or unexpected field ordering. Keploy records actual dependency responses from production traffic, so integration tests always reflect real-world behavior including the long-tail scenarios that cause production incidents.
During a recording session, Keploy's eBPF probes intercept every outbound network call — database queries (PostgreSQL, MongoDB, DynamoDB), downstream HTTP/gRPC calls, Redis commands, Kafka messages — and stores both the request and response. During replay, Keploy intercepts outbound calls at the kernel level and returns the recorded response, eliminating all external dependencies without any mock configuration files.
Unit testing validates a single function or class in isolation, mocking all external dependencies. Integration testing exercises real interactions between components, such as a service calling a database, an API calling a downstream service, or a message producer writing to a queue. Integration tests are slower but catch a different class of bugs.
Time-dependent logic (token expiry checks, cache TTLs, date-based partitioning) breaks during test replay because the system clock has advanced. Keploy freezes the system clock at the original recording timestamp during replay, so all time-dependent code paths produce identical results. No other integration testing tool offers kernel-level time manipulation without requiring application code changes.
Testcontainers provides language-specific libraries (Java, Go, .NET, Python, Node.js) that must be imported, configured, and maintained in each project. Keploy operates at the Linux kernel level, intercepting network syscalls regardless of the application language or framework. A Go service, a Java monolith, and a Python microservice can all use identical Keploy workflows with zero SDK dependencies.
Integration tests should run after unit tests in the CI pipeline. With Keploy, replayed integration tests execute in milliseconds because dependencies are served from recorded mocks, not live infrastructure. This means integration tests can gate every PR rather than being relegated to nightly runs, catching regressions before they reach the main branch.
A single recording session of production or staging traffic typically covers 70-90% of active service interactions including database queries, downstream API calls, and cache operations. Keploy's deduplication removes redundant test cases while preserving branch coverage. Teams achieve in hours what would take weeks of manual integration test authoring.
Flaky integration tests are primarily caused by non-deterministic data — timestamps, UUIDs, auto-increment IDs, and session tokens. Keploy's AI analyzes multiple recordings to identify fields that vary between runs and automatically excludes them from strict assertions. Combined with time-freezing, this eliminates the two root causes of integration test flakiness without any manual exclusion configuration.
Join the Keploy community
Follow updates, ask questions, share feedback, and ship faster with other Keploy builders.