От одного контейнера к полному стеку
В предыдущем уроке вы научились запускать тесты в одном Docker-контейнере. Но реальные приложения редко работают изолированно. Типичное веб-приложение требует базу данных, кэш, возможно очередь сообщений и, может быть, почтовый сервис. Docker Compose позволяет определить всё это как единый стек, который запускается и останавливается вместе.
Для QA Docker Compose — это трансформация. Вместо ручной настройки PostgreSQL, Redis и приложения перед запуском интеграционных тестов, вы определяете всё в файле docker-compose.yml и запускаете одной командой.
Основы Docker Compose
Минимальный пример
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgresql://test:test@db:5432/testdb
- REDIS_URL=redis://cache:6379
depends_on:
db:
condition: service_healthy
cache:
condition: service_started
db:
image: postgres:15
environment:
POSTGRES_DB: testdb
POSTGRES_USER: test
POSTGRES_PASSWORD: test
healthcheck:
test: ["CMD-SHELL", "pg_isready -U test"]
interval: 5s
timeout: 5s
retries: 5
cache:
image: redis:7-alpine
Запуск и остановка
# Запустить все сервисы
docker compose up -d
# Запустить и пересобрать образы
docker compose up -d --build
# Просмотр логов
docker compose logs -f
# Остановить и удалить контейнеры
docker compose down
# Остановить, удалить контейнеры И тома (чистое состояние)
docker compose down -v
Паттерны тестовых окружений
Паттерн 1: Приложение + Зависимости + Test Runner
Самый распространённый паттерн для интеграционного и E2E-тестирования:
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=test
- DATABASE_URL=postgresql://test:test@db:5432/testdb
depends_on:
db:
condition: service_healthy
db:
image: postgres:15
environment:
POSTGRES_DB: testdb
POSTGRES_USER: test
POSTGRES_PASSWORD: test
volumes:
- ./scripts/init-test-db.sql:/docker-entrypoint-initdb.d/init.sql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U test"]
interval: 5s
timeout: 5s
retries: 5
tests:
build:
context: .
dockerfile: Dockerfile.tests
environment:
- BASE_URL=http://app:3000
- DATABASE_URL=postgresql://test:test@db:5432/testdb
depends_on:
app:
condition: service_started
volumes:
- ./test-results:/app/test-results
Паттерн 2: Полный интеграционный стек
version: '3.8'
services:
app:
build: .
environment:
- DATABASE_URL=postgresql://test:test@db:5432/testdb
- REDIS_URL=redis://cache:6379
- MAIL_HOST=mailhog
- MAIL_PORT=1025
- S3_ENDPOINT=http://localstack:4566
depends_on:
db:
condition: service_healthy
db:
image: postgres:15
environment:
POSTGRES_DB: testdb
POSTGRES_USER: test
POSTGRES_PASSWORD: test
healthcheck:
test: ["CMD-SHELL", "pg_isready -U test"]
interval: 5s
timeout: 5s
retries: 5
cache:
image: redis:7-alpine
mailhog:
image: mailhog/mailhog
ports:
- "8025:8025"
localstack:
image: localstack/localstack
environment:
- SERVICES=s3
- DEFAULT_REGION=us-east-1
Health Checks
Health checks необходимы для надёжности тестов. Без них тесты могут начаться до того, как БД готова принимать соединения.
db:
image: postgres:15
healthcheck:
test: ["CMD-SHELL", "pg_isready -U test"]
interval: 5s
timeout: 5s
retries: 5
start_period: 10s
Упражнение: Создайте полное тестовое окружение
Спроектируйте docker-compose.yml для e-commerce приложения с:
- Backend API на Node.js
- Базой данных PostgreSQL (с начальными данными)
- Кэшем Redis
- Почтовым сервисом (для подтверждений заказов)
- Test runner для API и E2E-тестов
- Результатами тестов, сохранёнными на хост-машине
Решение
version: '3.8'
services:
api:
build:
context: .
dockerfile: Dockerfile
ports:
- "3000:3000"
environment:
- NODE_ENV=test
- DATABASE_URL=postgresql://test:test@db:5432/ecommerce_test
- REDIS_URL=redis://cache:6379
- MAIL_HOST=mailhog
- MAIL_PORT=1025
depends_on:
db:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 5s
timeout: 5s
retries: 10
db:
image: postgres:15
environment:
POSTGRES_DB: ecommerce_test
POSTGRES_USER: test
POSTGRES_PASSWORD: test
volumes:
- ./scripts/seed-test-data.sql:/docker-entrypoint-initdb.d/01-seed.sql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U test"]
interval: 5s
timeout: 5s
retries: 5
cache:
image: redis:7-alpine
mailhog:
image: mailhog/mailhog
ports:
- "8025:8025"
api-tests:
build:
context: .
dockerfile: Dockerfile.tests
environment:
- BASE_URL=http://api:3000
- MAILHOG_URL=http://mailhog:8025
command: npm run test:api
depends_on:
api:
condition: service_healthy
volumes:
- ./test-results/api:/app/test-results
e2e-tests:
image: mcr.microsoft.com/playwright:v1.40.0-focal
working_dir: /app
volumes:
- .:/app
- ./test-results/e2e:/app/test-results
environment:
- BASE_URL=http://api:3000
command: npx playwright test
depends_on:
api:
condition: service_healthy
Использование:
# Запуск API-тестов
docker compose run --rm api-tests
# Запуск E2E-тестов
docker compose run --rm e2e-tests
# Очистка
docker compose down -v
Лучшие практики
Всегда используйте health checks с
depends_on: condition: service_healthy. Одного порядка запуска недостаточно.Используйте
docker compose down -vпосле тестов. Флаг-vудаляет тома, обеспечивая чистое состояние для каждого запуска.Монтируйте результаты тестов как тома. Это делает отчёты доступными на хост-машине и в CI-артефактах.
Храните тестовые данные в SQL seed-файлах, монтируемых через
docker-entrypoint-initdb.d/. Это обеспечивает консистентные, воспроизводимые тестовые данные.Используйте
docker compose run --rmдля выполнения тестов. Флаг--rmудаляет контейнер после завершения.Фиксируйте версии образов. Используйте
postgres:15вместоpostgres:latestдля предотвращения неожиданных изменений поведения.