Tags: testing, qa, backend, api, microservices, database
Level: intermediate
TL;DR: Integration testing verifies that different system modules or services work together correctly. It catches issues in data flow, API contracts, and database interactions that unit tests miss.
Focuses on the interfaces and data flow between interconnected components.
Requires a test environment closely mirroring production conditions.
Validates database schemas, complex queries, and transaction management against a real data store.
Verifies API contracts, HTTP response codes, and network payloads between microservices.
Slower than unit tests but faster and much more reliable than full end-to-end (E2E) tests.
Frequently uses tools like Testcontainers to spin up isolated databases or message brokers.
Mocks external third-party APIs to prevent flaky tests caused by network latency or rate limits.
Q: What is the main difference between unit testing and integration testing?R: Unit testing isolates a single function using mocks, while integration testing verifies that actual components, like my service and a database, communicate correctly.
Q: How do you handle databases in integration tests?R: I use ephemeral test databases spun up via Testcontainers before the test suite runs, ensuring a clean state and realistic interactions without affecting other environments.
Q: When should you mock an external dependency in an integration test?R: I mock third-party APIs that I do not control to avoid network unreliability and rate limits, while keeping internal service interactions real whenever possible.
Using shared remote databases across parallel test runs, which leads to race conditions and flaky tests.
Failing to clean up, rollback, or reset the database state between individual test cases.
Testing detailed business logic that should be covered by unit tests, making the suite unnecessarily slow.
Relying on external staging environments that can change unpredictably, rather than using reproducible local fixtures.
// Example: Supertest with Jest for an Express API integration test
const request = require('supertest');
const app = require('../src/app');
const db = require('../src/db');
beforeAll(async () => {
await db.migrate.latest();
});
beforeEach(async () => {
await db('users').truncate();
});
afterAll(async () => {
await db.destroy();
});
test('POST /users creates a new user and persists it to the database', async () => {
const response = await request(app)
.post('/users')
.send({ email: 'test@example.com', name: 'John Doe' });
expect(response.status).toBe(201);
expect(response.body).toHaveProperty('id');
const dbUser = await db('users').where({ email: 'test@example.com' }).first();
expect(dbUser).toBeDefined();
expect(dbUser.name).toBe('John Doe');
});