Integration Testing TypeScript with Docker: Advanced Level
Integration testing is a key stage in the development of modern TypeScript applications. When your system consists of many microservices, databases, message queues, and external APIs, isolated unit tests no longer cover real interaction scenarios. Docker allows you to create reproducible, isolated testing environments that are close to production. In this article, we will explore advanced techniques for integration testing TypeScript applications using Docker: from setting up containers to building CI/CD pipelines.
Why Docker is an Ideal Tool for TypeScript Integration Tests
Traditional approaches to integration testing often suffer from environment issues: "it works on my machine." Docker solves this problem by providing:
- Isolation — each test runs in a clean container, without affecting other processes
- Reproducibility — the same environment at all stages: locally, in CI, on staging
- Speed — lightweight containers start in seconds
- Flexibility — easily combine different versions of databases, caches, queues
For TypeScript projects, Docker is especially useful as it allows testing not only the Node.js runtime but also interaction with the TypeScript compiler, transpilation, and build process.
Setting Up a Docker Environment for TypeScript Tests
Let's start with the basic configuration. We'll create a Dockerfile for the production build and a separate one for tests:
# Dockerfile.testFROM node:18-alpine AS builder
WORKDIR /appCOPY package*.json ./RUN npm ciCOPY tsconfig.json ./COPY src/ ./src/
# Build TypeScriptRUN npm run build
# Stage for testsFROM node:18-alpine AS test-runnerWORKDIR /appCOPY --from=builder /app/dist ./distCOPY --from=builder /app/node_modules ./node_modulesCOPY --from=builder /app/package.json ./COPY tests/ ./tests/COPY jest.config.ts ./
# Run integration testsCMD ["npx", "jest", "--config", "jest.config.ts", "--testMatch", "**/*.integration.test.ts"]To manage multiple services, we use docker-compose.yml:
version: '3.8'services: app: build: context: . dockerfile: Dockerfile.test depends_on: postgres: condition: service_healthy redis: condition: service_healthy environment: - DB_HOST=postgres - DB_PORT=5432 - REDIS_HOST=redis - REDIS_PORT=6379 volumes: - ./tests:/app/tests:ro networks: - test-network
postgres: image: postgres:15-alpine environment: POSTGRES_DB: testdb POSTGRES_USER: testuser POSTGRES_PASSWORD: testpass healthcheck: test: ["CMD-SHELL", "pg_isready -U testuser -d testdb"] interval: 5s timeout: 5s retries: 5 networks: - test-network
redis: image: redis:7-alpine healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 5s timeout: 3s retries: 5 networks: - test-network
networks: test-network: driver: bridgeWriting Advanced Integration Tests in TypeScript
Now let's create a test that checks a complete scenario: writing to the database, caching in Redis, and sending an event to a queue. We'll use the testcontainers library for dynamic container management from TypeScript code:
// tests/integration/user-registration.integration.test.tsimport { GenericContainer, StartedTestContainer, Wait } from 'testcontainers';import { PostgreSqlContainer } from '@testcontainers/postgresql';import { RedisContainer } from '@testcontainers/redis';import axi