Integration Testing JavaScript with Docker: Advanced Level
Integration testing is a key stage in modern web application development. It allows you to verify the interaction between various system components: the database, cache, external APIs, and the application itself. Docker makes this process predictable, isolated, and reproducible. In this article, we will explore advanced techniques for integration testing JavaScript (Node.js) using Docker.
Why Docker in Integration Testing?
Traditional integration testing faces challenges:
- Differences between developer, CI, and production environments.
- Data pollution in shared databases.
- Complexity of running dependencies (Redis, PostgreSQL, Kafka).
Docker solves these problems by providing lightweight, isolated containers. Each test can launch its own infrastructure, destroying it upon completion. This guarantees clean and repeatable results.
Setting Up a Test Environment with Docker Compose
For advanced testing, we will use a docker-compose.yml that describes all the services needed for tests: the application itself, the database, cache, and mock servers.
Example docker-compose.test.yml:
version: '3.8'services: app: build: . environment: - NODE_ENV=test - DB_HOST=postgres_test - REDIS_HOST=redis_test - MOCK_SERVER_URL=http://mock-server:8080 depends_on: - postgres_test - redis_test - mock-server command: ["npm", "run", "test:integration"]
postgres_test: image: postgres:15-alpine environment: POSTGRES_DB: testdb POSTGRES_USER: test POSTGRES_PASSWORD: test tmpfs: /var/lib/postgresql/data
redis_test: image: redis:7-alpine tmpfs: /data
mock-server: image: mockserver/mockserver:latest environment: MOCKSERVER_LOG_LEVEL: ERRORKey points:
- tmpfs: Database and Redis data are stored in RAM, speeding up tests.
- mock-server: An isolated service for simulating external APIs.
- NODE_ENV=test: Switches the application to test configuration.
Writing Integration Tests with Testcontainers
The testcontainers library for Node.js allows you to programmatically manage containers directly from tests. This provides flexibility: you can run specific containers for each test suite.
Installation:
npm install --save-dev testcontainersExample test with PostgreSQL:
const { GenericContainer } = require('testcontainers');const { Client } = require('pg');
describe('User Repository Integration Test', () => { let container; let client;
beforeAll(async () => { // Start PostgreSQL container container = await new GenericContainer('postgres:15-alpine') .withEnvironment({ POSTGRES_DB: 'testdb', POSTGRES_USER: 'test', POSTGRES_PASSWORD: 'test' }) .withExposedPorts(5432) .start();
// Connect to the container client = new Client({ host: container.getHost(), port: container.getMappedPort(5432), database: 'testdb', user: 'test', password: 'test' }); await client.connect();
// Create schema await client.query(` CREATE TABLE users ( id SERIAL PRIMARY KEY, name VARCHAR(100), email VARCHAR(100) UNIQUE ) `); });
afterAll(async () => { await client.end(); await container.stop(); });
test('should insert and retrieve user', async () => { await client.query(