Интеграционное тестирование JavaScript с Docker: полное руководство

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Интеграционное тестирование JavaScript с Docker: от А до Я

В современной веб-разработке качество кода — это не просто метрика, а залог стабильности продукта. Интеграционное тестирование (Integration Testing) занимает ключевое место в пирамиде тестирования, позволяя проверить взаимодействие между различными модулями, базами данных, внешними API и сервисами. Однако настройка изолированных и воспроизводимых сред для таких тестов часто становится головной болью. Здесь на помощь приходит Docker — инструмент контейнеризации, который идеально подходит для создания чистых, повторяемых тестовых окружений.

В этой статье мы разберём, как организовать интеграционное тестирование для JavaScript (Node.js) приложений с использованием Docker. Вы узнаете, как поднять тестовую базу данных, запустить зависимости в контейнерах и написать эффективные тесты, которые можно легко интегрировать в пайплайн CI/CD.

1. Почему Docker идеален для интеграционных тестов?

Традиционный подход к интеграционному тестированию часто требует ручной настройки окружения на каждой машине разработчика или сервере CI. Это приводит к проблеме "на моей машине работает". Docker решает эту проблему кардинально:

  • Изоляция: Каждый тест запускается в чистом контейнере, что исключает влияние побочных эффектов от предыдущих запусков.
  • Воспроизводимость: Одна и та же среда (версия Node.js, базы данных, системные библиотеки) гарантированно работает везде: на локальной машине разработчика, на сервере сборки и в продакшене.
  • Управление зависимостями: Вы можете легко поднять сложные инфраструктуры (Redis, PostgreSQL, RabbitMQ) одной командой docker-compose up -d.
  • Скорость: Контейнеры запускаются за секунды, в отличие от виртуальных машин. Docker кэширует слои, что ускоряет повторные сборки.

В контексте Node.js, Docker позволяет "замокать" внешние сервисы, не покидая среды разработки. Вместо того чтобы стучаться в продакшен-API, вы поднимаете тестовый контейнер с, например, json-server или testcontainers.

2. Настройка проекта Node.js для тестирования

Предположим, у нас есть простое Express-приложение, которое сохраняет пользователей в PostgreSQL. Наша задача — протестировать эндпоинт POST /users.

Структура проекта:

project/
├── src/
│   ├── app.js
│   ├── db.js
│   └── routes/
│       └── users.js
├── test/
│   └── integration/
│       └── users.test.js
├── docker-compose.test.yml
├── Dockerfile
├── package.json
└── .env.test

Для тестов мы будем использовать популярный стек: Mocha (фреймворк) + Chai (библиотека утверждений) + Supertest (HTTP-тестирование).

Установим зависимости:

npm install --save-dev mocha chai supertest

Файл package.json должен содержать скрипт для запуска тестов:

{
  "scripts": {
    "test:integration": "mocha test/integration/**/*.test.js --timeout 10000"
  }
}

3. Создание Docker-окружения для тестов

Ключевой файл — docker-compose.test.yml. Он описывает сервисы, необходимые для тестов.

version: '3.8'

services:
  app-test:
    build:
      context: .
      dockerfile: Dockerfile
    environment:
      - NODE_ENV=test
      - DB_HOST=postgres-test
      - DB_PORT=5432
      - DB_USER=test_user
      - DB_PASSWORD=test_pass
      - DB_NAME=test_db
    depends_on:
      postgres-test:
        condition: service_healthy
    command: ["npm", "run", "test:integration"]

  postgres-test:
    image: postgres:15-alpine
    environment:
      POSTGRES_USER: test_user
      POSTGRES_PASSWORD: test_pass
      POSTGRES_DB: test_db
    ports:
      - "5433:5432" # Пробрасываем на другой порт, чтобы не конфликтовать с локальной БД
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U test_user -d test_db"]
      interval: 5s
      timeout: 5s
      retries: 5

Обратите внимание на healthcheck. Мы не можем запускать тесты, пока PostgreSQL не будет готова принимать соединения. Docker Compose ждет, пока контейнер с БД не станет "здоровым".

Dockerfile для приложения:

FROM node:18-alpine

WORKDIR /app

COPY package*.json ./
RUN npm ci --only=production

COPY . .

CMD ["node", "src/app.js"]

4. Написание интеграционных тестов

Теперь самое интересное — код теста. Мы используем паттерн setup/teardown для управления состоянием базы данных.

Файл: test/integration/users.test.js

const request = require('supertest');
const { expect } = require('chai');
const app = require('../../src/app');
const { initDb, closeDb, clearDb } = require('../../src/db');

describe('POST /users', () => {
  before(async () => {
    // Подключаемся к тестовой БД (настройки берутся из переменных окружения Docker)
    await initDb();
  });

  after(async () => {
    // Закрываем соединение после всех тестов
    await closeDb();
  });

  beforeEach(async () => {
    // Очищаем таблицу перед каждым тестом для изоляции
    await clearDb('users');
  });

  it('должен создать нового пользователя и вернуть статус 201', async () => {
    const newUser = {
      name: 'Иван Иванов',
      email: 'ivan@example.com'
    };

    const response = await request(app)
      .post('/users')
      .send(newUser)
      .expect('Content-Type', /json/)
      .expect(201);

    // Проверяем структуру ответа
    expect(response.body).to.have.property('id');
    expect(response.body.name).to.equal(newUser.name);
    expect(response.body.email).to.equal(newUser.email);
  });

  it('должен вернуть ошибку 400 при отсутствии email', async () => {
    const invalidUser = { name: 'Петр Петров' };

    const response = await request(app)
      .post('/users')
      .send(invalidUser)                

Блоги

Рекомендаций по книгам