Integration Testing Explained: From APIs to Distributed Systems

 

Integration Testing Explained: From APIs to Distributed Systems

Introduction



Modern software applications rarely operate as isolated components. A typical application may consist of a frontend, backend services, databases, third-party APIs, payment gateways, message queues, and other external systems.

Each component might work perfectly in isolation, but problems can still occur when these components interact.

For example, an insurance application might successfully calculate a premium, process a payment, and generate a policy document when each function is tested independently. However, what happens if the payment service sends an unexpected response? What if the policy service receives incomplete data? Or what if a background process fails to update the policy after a successful payment?

These are the types of problems integration testing is designed to identify.

But integration testing is not limited to testing two modules together. Depending on the architecture and scope of the application, it can take place at several levels.

1. Component-Level Integration Testing

Component-level integration testing focuses on the interactions between closely related components within an application.

Unlike unit testing, which typically verifies a component in isolation, this level checks whether multiple components work correctly together.

Example:

Consider an insurance premium calculation feature consisting of:

  • A premium calculation module.

  • A discount calculation module.

  • A tax calculation module.

Each module might pass its unit tests independently. Component-level integration testing verifies that the calculated discount is correctly passed to the premium calculation module and that the tax is applied to the correct amount.

What should we test?

  • Data passed between components.

  • Correct handling of component outputs.

  • Error handling when a dependent component fails.

  • Compatibility between component interfaces.

  • Business rules spanning multiple components.

Automation opportunities: These tests can often be automated using unit-testing frameworks, component test frameworks, or lightweight integration test suites.

2. Module or Service-Level Integration Testing

This level focuses on interactions between modules or services that have their own responsibilities and interfaces.

In a layered application, this might involve the business logic layer communicating with the data access layer. In a service-oriented application, it might involve one backend service communicating with another.

Example:

An insurance policy service retrieves customer information from a customer service before creating a policy.

The integration tests should verify that:

  • The policy service sends the correct customer identifier.

  • The customer service returns the expected data structure.

  • Missing or invalid customer data is handled correctly.

  • Errors and timeouts are handled appropriately.

  • The policy is not created with inconsistent customer information.

Automation opportunities: API tests, database integration tests, and tests using real or controlled service dependencies are useful at this level.

3. API Integration Testing

API integration testing verifies whether systems or services communicate correctly through their APIs.

This is particularly important in applications where the frontend, backend, and external services communicate through REST, GraphQL, or other API protocols.

Example:

A frontend submits a request to create an insurance quote. The backend validates the request, retrieves customer information, calculates the premium, and returns the quote.

API integration tests can verify:

  • HTTP status codes and response payloads.

  • Request and response schemas.

  • Authentication and authorization.

  • Data validation and error responses.

  • Business rules across connected services.

  • Backward compatibility when API contracts change.

It is important to remember that an API test is not automatically an integration test. An API test that targets a single service with all dependencies mocked may verify service behaviour without testing the actual integrations.

The scope depends on which components are genuinely interacting during the test.

4. Database Integration Testing

Database integration testing checks whether application components interact correctly with the database.

A service might return the correct response, but the underlying data could still be incorrect, incomplete, or inconsistent.

Example:

After a customer purchases an insurance policy, the application should save the policy details, premium amount, payment status, and relevant customer information.

Tests should verify that:

  • Data is persisted correctly.

  • Database transactions commit or roll back as expected.

  • Relationships and constraints are maintained.

  • Duplicate requests do not create unintended duplicate records.

  • Concurrent operations do not cause inconsistent data.

  • Database errors are handled safely.

Database integration testing is particularly important for applications involving financial transactions, insurance policies, and other business-critical records.

5. External System Integration Testing

Most business applications depend on external systems such as payment gateways, identity providers, email services, and third-party data providers.

External system integration testing verifies that the application interacts correctly with these dependencies.

Example:

When a customer pays an insurance premium, the application sends a payment request to a payment provider.

The test scenarios should include:

  • Successful payment.

  • Declined payment.

  • Invalid payment details.

  • Timeout or unavailable provider.

  • Duplicate payment requests.

  • Unexpected or malformed responses.

  • Successful payment followed by a failure to update the policy.

The last scenario is particularly important because a successful response from an external provider does not necessarily mean the entire business process has completed successfully.

Where possible, use a provider's sandbox environment for controlled integration tests. Contract tests and simulated responses can supplement these tests, but they do not completely replace testing against the real provider.

6. End-to-End Integration Testing

End-to-end integration testing verifies that multiple components and systems work together across a complete business workflow.

Although it overlaps with end-to-end functional testing, the emphasis here is on validating the integrations and data flow between systems.

Example: An insurance purchase journey

  1. A customer enters personal details.

  2. The application retrieves relevant customer information.

  3. The premium calculation service generates a quote.

  4. The customer accepts the quote.

  5. The payment provider processes the payment.

  6. The policy service creates or activates the policy.

  7. A notification service sends confirmation to the customer.

The test verifies not only that each step succeeds, but also that the correct data flows between systems and the final business outcome is consistent.

Automation opportunities: Cypress or Playwright can automate browser-based journeys, while API automation can validate backend workflows more directly.

End-to-end tests are valuable, but they tend to be slower, more expensive to maintain, and more vulnerable to environmental issues than narrower integration tests. They should complement—not replace—lower-level integration testing.

7. Distributed System and Event-Driven Integration Testing

Modern architectures increasingly use microservices, asynchronous messaging, event streams, and background workers.

In these systems, services may communicate through message brokers or events rather than immediate API responses.

This creates additional integration risks that traditional request-response testing may not cover.

Example:

After a payment succeeds, a payment service publishes a payment-completed event. A background worker processes the event and updates the insurance policy. Another service sends a confirmation email.

Tests should verify:

  • Events contain the correct information.

  • Consumers process events correctly.

  • Messages are not lost under expected operating conditions.

  • Duplicate messages do not cause duplicate business actions.

  • Retry and dead-letter handling work correctly.

  • Events arriving out of order are handled appropriately.

  • Eventual consistency is achieved within acceptable time limits.

These tests are especially important when systems use technologies such as Kafka, RabbitMQ, cloud queues, or asynchronous workers.

How Do These Levels Fit Together?

The levels described above are useful testing scopes, not a universally standardised sequence. They can overlap, and teams do not necessarily need to implement every level as a separate test phase.

The traditional integration testing approaches—big bang, top-down, bottom-up, and sandwich (hybrid)—describe how components are progressively integrated and tested. They are different from the application and architecture levels discussed in this article.

For example:

  • Top-down integration testing: Start with higher-level components and use stubs for components that are not yet available.

  • Bottom-up integration testing: Start with lower-level components and use drivers to simulate higher-level components.

  • Big-bang integration testing: Integrate many components together before testing their interactions.

  • Sandwich integration testing: Combine top-down and bottom-up approaches.

These approaches can be relevant when planning integration testing, particularly when components are developed independently or become available at different times.

Building an Effective Integration Testing Strategy

An effective integration testing strategy does not mean testing every possible combination at every level. It means selecting the right level of testing based on risk, architecture, and business impact.

A practical strategy should consider the following:

1. Identify critical integration points.

Map the communication paths between services, databases, external providers, and asynchronous processes.

2. Prioritise business-critical workflows.

Payment processing, premium calculation, policy creation, refunds, and claims processing deserve particular attention because failures can have significant business consequences.

3. Test failure scenarios as well as successful ones.

Timeouts, invalid responses, unavailable dependencies, duplicate requests, and partial failures are often more revealing than happy-path testing alone.

4. Use test doubles appropriately.

Mocks, stubs, and service virtualisation can make tests faster and more predictable. However, maintain tests that exercise genuine integrations so that configuration, networking, authentication, and actual dependency behaviour are not overlooked.

5. Automate at the appropriate level.

Keep fast, focused tests close to the components being integrated. Use broader end-to-end tests for critical business journeys.

6. Integrate testing into CI/CD.

Run suitable integration tests as part of the delivery pipeline. The scope of testing can vary by pull request, deployment stage, and release risk.

7. Measure more than test pass rates.

Track escaped integration defects, critical workflow coverage, flaky-test rates, integration failure patterns, and the time required to diagnose failures.

Conclusion

Integration testing is essential because software quality depends on more than the correctness of individual components. It also depends on how those components communicate, exchange data, handle failures, and maintain consistency.

From component-level integration to distributed event-driven workflows, each testing scope addresses a different set of risks.

For QA engineers and test leads, the goal is not to maximise the number of integration tests. It is to build a balanced strategy that detects integration failures early, provides reliable feedback, and protects critical business processes.

Ultimately, the best integration testing strategy is one that reflects the application's architecture and business risks—not simply one that follows a predefined testing pyramid.

Comments