Integrationstests von TypeScript mit Docker: von A bis Z
\n\nModerne TypeScript-Entwicklung erfordert nicht nur qualitativ hochwertige Unit-Tests, sondern auch die Überprüfung der Interaktion von Komponenten unter realen Bedingungen. Integrationstests mit Docker ermöglichen es, isolierte Umgebungen mit Datenbanken, Caches und anderen Diensten zu erstellen, was die Tests so nah wie möglich an die Produktion bringt. In diesem Artikel werden wir erläutern, wie man Integrationstests für eine TypeScript-Anwendung mit Docker einrichtet, Tests für die REST-API schreibt und Best Practices betrachtet.
\n\nWarum Docker für Integrationstests?
\n\nDocker löst eines der Hauptprobleme von Integrationstests – die Abhängigkeit von externen Diensten. Anstatt eine gemeinsame Datenbank oder Redis zu verwenden, die von anderen Entwicklern belegt sein könnten, erstellen wir für jeden Testlauf isolierte Container. Dies garantiert:
\n\n- \n
- Sauberkeit der Tests: jeder Start beginnt mit einem leeren Zustand \n
- Wiederholbarkeit: Tests funktionieren auf jedem Rechner gleich \n
- Isolation: Tests beeinflussen sich weder gegenseitig noch Produktionsdaten \n
- Geschwindigkeit: Container werden in Sekunden gestartet \n
Einrichtung des Projekts und Docker Compose
\n\nWir erstellen ein einfaches Projekt mit TypeScript, Express und PostgreSQL. Unsere Aufgabe ist es, den Endpunkt zum Erstellen eines Benutzers zu testen. Beginnen wir mit der Datei docker-compose.test.yml:
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\nJetzt erstellen wir Dockerfile.test zum Bauen der TypeScript-Anwendung:
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\nSchreiben von Integrationstests mit Jest und Supertest
\n\nInstallieren wir die erforderlichen Pakete:
\n\nnpm install --save-dev jest @types/jest ts-jest supertest @types/supertest\n\nErstellen wir die Jest-Konfiguration (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\nDie Datei jest.setup.ts ist für das Starten und Stoppen der Docker-Container verantwortlich:
import { execSync, spawn } from 'child_process';\nimport path from 'path';\n\nconst composeFile = path.resolve(__dirname, '../docker-compose.test.yml');\n\nbeforeAll(async () => {\n // Container starten\n execSync(`docker-compose -f ${composeFile} up -d --build`, { stdio: 'inherit' });\n \n // Warten, bis die Anwendung verfügbar ist\n await new Promise((resolve) => setTimeout(resolve, 5000));\n});\n\nafterAll(async () => {\n // Container stoppen und entfernen\n execSync(`docker-compose -f ${composeFile} down -v`, { stdio: 'inherit' });\n});\n\nJetzt schreiben wir den eigentlichen Test. Erstellen wir die Datei __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