Integration Testing TypeScript with Docker: From A to Z
\n\nModern TypeScript development requires not only high-quality unit testing but also verification of component interaction in real-world conditions. Integration testing using Docker allows you to spin up isolated environments with databases, caches, and other services, making tests as close to production as possible. In this article, we will walk through how to set up integration testing for a TypeScript application using Docker, write tests for a REST API, and review best practices.
\n\nWhy Docker for Integration Testing?
\n\nDocker solves one of the main problems of integration tests — dependency on external services. Instead of using a shared database or Redis that might be occupied by other developers, we spin up isolated containers for each test run. This guarantees:
\n\n- \n
- Test cleanliness: each run starts from a clean state \n
- Repeatability: tests work the same way on any machine \n
- Isolation: tests do not affect each other or production data \n
- Speed: containers spin up in seconds \n
Project Setup and Docker Compose
\n\nLet's create a simple TypeScript project with Express and PostgreSQL. Our goal is to test a user creation endpoint. We'll start with the docker-compose.test.yml file:
version: '3.8'\nservices:\n db_test:\n image: postgres:15-alpine\n environment:\n POSTGRES_DB: testdb\n POSTGRES_USER: testuser\n POSTGRES_PASSWORD: testpass\n ports:\n - "5433:5432"\n healthcheck:\n test: ["CMD-SHELL", "pg_isready -U testuser -d testdb"]\n interval: 5s\n timeout: 5s\n retries: 5\n\n app_test:\n build:\n context: .\n dockerfile: Dockerfile.test\n environment:\n DATABASE_URL: postgres://testuser:testpass@db_test:5432/testdb\n NODE_ENV: test\n ports:\n - "3001:3000"\n depends_on:\n db_test:\n condition: service_healthy\n\nNow let's create a Dockerfile.test to build the TypeScript application:
FROM node:18-alpine\nWORKDIR /app\nCOPY package*.json ./\nRUN npm ci --only=production\nCOPY . .\nRUN npm run build\nCMD ["node", "dist/index.js"]\n\nWriting Integration Tests with Jest and Supertest
\n\nInstall the necessary packages:
\n\nnpm install --save-dev jest @types/jest ts-jest supertest @types/supertest\n\nCreate a Jest configuration (jest.config.ts):
export default {\n preset: 'ts-jest',\n testEnvironment: 'node',\n testMatch: ['**/__tests__/**/*.test.ts'],\n setupFilesAfterSetup: ['./jest.setup.ts'],\n testTimeout: 30000,\n};\n\nThe jest.setup.ts file will handle starting and stopping Docker containers:
import { execSync, spawn } from 'child_process';\nimport path from 'path';\n\nconst composeFile = path.resolve(__dirname, '../docker-compose.test.yml');\n\nbeforeAll(async () => {\n // Spin up containers\n execSync(`docker-compose -f ${composeFile} up -d --build`, { stdio: 'inherit' });\n \n // Wait for the application to become available\n await new Promise((resolve) => setTimeout(resolve, 5000));\n});\n\nafterAll(async () => {\n // Stop and remove containers\n execSync(`docker-compose -f ${composeFile} down -v`, { stdio: 'inherit' });\n});\n\nNow let's write the actual test. Create a file __tests__/users.test.ts:
import request from 'supertest';\n\nconst BASE_URL = 'http://localhost:3001/api';\n\ndescribe('Users API Integration Tests', () => {\n const testUser = {\n name: 'Ivan Ivanov',\n email: 'ivan@example.com',\n password: 'securePass123'\n };\n\n