Part 3: Integration Testing

Introduction

Integration tests verify that different components of your system work together correctly. While unit tests check individual functions in isolation, integration tests ensure components interact properlyβ€”database operations, API calls, message queues, and external services.

In my TypeScript microservices, integration tests have caught countless bugs that unit tests missed: incorrect SQL queries, misconfigured database connections, serialization issues, and timing problems.

What is Integration Testing?

Integration testing focuses on:

  • Component interactions - How services communicate

  • Data persistence - Database read/write operations

  • External services - APIs, message queues, caches

  • Configuration - Environment-specific settings

  • Data flow - Information moving between layers

Example Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   API Layer β”‚
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
       β”‚
β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”
β”‚ Service Layerβ”‚
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
       β”‚
β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚Repository Layerβ”‚
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
       β”‚
β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”
β”‚   Database   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Integration tests verify each layer communicates correctly.

Database Integration Testing

Setting Up Test Database

I use a separate test database for integration tests:

database.config.ts:

Repository Integration Tests

user-repository.ts:

Integration tests:

Service Layer Integration Tests

Service layer integration tests verify business logic with real dependencies.

order-service.ts:

Integration tests:

Testing with Docker Containers

For more realistic integration tests, I use Docker containers with Testcontainers.

Installation:

Database container setup:

Using containers in tests:

Testing Message Queues

Integration tests for event-driven systems with RabbitMQ or Redis.

Example with Redis:

Integration tests:

Integration Test Best Practices

1. Use Transactions for Cleanup

2. Test Data Builders

3. Separate Test Suites

Key Takeaways

  1. Integration tests verify component interactions - Not just isolated functions

  2. Use real dependencies when possible - Databases, message queues

  3. Testcontainers for consistency - Same environment everywhere

  4. Clean up test data - Use transactions or truncate tables

  5. Test error scenarios - Network failures, timeouts, constraints

  6. Keep tests independent - Each test should run in isolation

  7. Balance speed vs realism - Not everything needs full integration

What's Next?

In Part 4: API Testing & Contract Testing, we'll cover:

  • HTTP endpoint testing

  • OpenAPI/Swagger validation

  • Contract testing with Pact

  • Testing authentication/authorization

  • API versioning tests


This article is part of the Software Testing 101 series.

Last updated