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

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

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

Начать курс

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

Интеграционное тестирование — ключевой этап разработки любого серьезного C++ проекта. Оно позволяет проверить, как отдельные модули системы работают вместе, взаимодействуют с базами данных, сетевыми сервисами и файловыми системами. Docker в этом контексте становится незаменимым инструментом: он обеспечивает изолированное, воспроизводимое окружение, исключая проблемы с "оно работает на моей машине". В этой статье мы разберем полный цикл настройки интеграционного тестирования C++ с Docker — от базовых концепций до продвинутых практик.

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

Традиционные интеграционные тесты C++ часто страдают от проблем окружения: разные версии библиотек, несовместимые компиляторы, специфичные настройки ОС. Docker решает эти проблемы раз и навсегда. Основные преимущества:

  • Воспроизводимость: каждый тест запускается в идентичном окружении, независимо от хост-системы.
  • Изоляция: контейнеры не влияют друг на друга и на хост-систему.
  • Масштабируемость: легко запускать сотни тестов параллельно.
  • CI/CD интеграция: контейнеры идеально вписываются в пайплайны Jenkins, GitLab CI, GitHub Actions.

Рассмотрим простой пример: ваше C++ приложение использует PostgreSQL. Без Docker вам пришлось бы устанавливать и настраивать PostgreSQL на каждом тестовом сервере. С Docker это одна строка конфигурации.

2. Настройка Docker-окружения для C++ проекта

Начнем с создания Dockerfile для нашего C++ приложения. Предположим, проект использует CMake и Google Test для unit-тестов, но нам нужны интеграционные тесты с реальной базой данных.

Базовый Dockerfile для сборки и тестирования:

# Dockerfile
FROM ubuntu:22.04 AS builder

RUN apt-get update && apt-get install -y \
    build-essential \
    cmake \
    git \
    libpq-dev \
    libgtest-dev \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app
COPY . .

RUN cmake -B build -DCMAKE_BUILD_TYPE=Release \
    && cmake --build build --parallel

# Стадия для тестов
FROM builder AS tester
CMD ["./build/integration_tests"]

Docker Compose для запуска инфраструктуры:

# docker-compose.yml
version: '3.8'

services:
  db:
    image: postgres:15
    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

  app:
    build: .
    depends_on:
      db:
        condition: service_healthy
    environment:
      DB_HOST: db
      DB_PORT: 5432
      DB_NAME: testdb
      DB_USER: testuser
      DB_PASSWORD: testpass
    command: ["./build/integration_tests"]

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

Рассмотрим пример интеграционного теста, который проверяет взаимодействие с PostgreSQL через библиотеку libpqxx. Предположим, у нас есть модуль для работы с пользователями.

Пример интеграционного теста:

// integration_tests.cpp
#include <gtest/gtest.h>
#include <pqxx/pqxx>
#include <string>
#include <vector>

class DatabaseIntegrationTest : public ::testing::Test {
protected:
    void SetUp() override {
        conn = new pqxx::connection(
            "host=" + std::getenv("DB_HOST") +
            " port=" + std::getenv("DB_PORT") +
            " dbname=" + std::getenv("DB_NAME") +
            " user=" + std::getenv("DB_USER") +
            " password=" + std::getenv("DB_PASSWORD")
        );
        
        // Создаем тестовую таблицу
        pqxx::work txn(*conn);
        txn.exec("CREATE TABLE IF NOT EXISTS users ("
                 "id SERIAL PRIMARY KEY, "
                 "name VARCHAR(100), "
                 "email VARCHAR(100) UNIQUE)");
        txn.commit();
    }

    void TearDown() override {
        // Очищаем данные после тестов
        pqxx::work txn(*conn);
        txn.exec("DROP TABLE IF EXISTS users CASCADE");
        txn.commit();
        delete conn;
    }

    pqxx::connection* conn;
};

// Функция, которую тестируем
bool addUser(pqxx::connection& conn, 
             const std::string& name, 
             const std::string& email) {
    try {
        pqxx::work txn(conn);
        txn.exec_params(
            "INSERT INTO users (name, email) VALUES ($1, $2)",
            name, email
        );
        txn.commit();
        return true;
    } catch (const std::exception& e) {
        return false;
    }
}

TEST_F(DatabaseIntegrationTest, AddUserSuccess) {
    ASSERT_TRUE(addUser(*conn, "John Doe", "john@example.com"));
    
    pqxx::work txn(*conn);
    auto result = txn.exec("SELECT COUNT(*) FROM users");
    ASSERT_EQ(result[0][0].as<int>(), 1);
}

TEST_F(DatabaseIntegrationTest, AddDuplicateEmail) {
    addUser(*conn, "John", "john@example.com");
    ASSERT_FALSE(addUser(*conn, "Jane", "john@example.com"));
}

TEST_F(DatabaseIntegrationTest, EmptyDatabase) {
    pqxx::work txn(*conn);
    auto result = txn.exec("SELECT COUNT(*) FROM users");
    ASSERT_EQ(result[0][0].as<int>(), 0);
}

4. Запуск тестов и интеграция с CI/CD

Теперь, когда у нас есть Docker-окружение и тесты, настроим их запуск. Создадим Makefile для удобства:

# Makefile
.PHONY: build test clean

build:
    docker compose build

test:
    docker compose up --abort-on-container-exit --exit-code-from app

clean:
    docker compose down -v

Интеграция с GitHub Actions:

# .github/workflows/integration-tests.yml
name: Integration Tests

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Build and run integration tests
      run: |
        docker compose build
        docker compose up --abort-on-container-exit --exit-code-from app
    
    - name: Cleanup
      if: always()
      run: docker compose down -v

5. Лучшие практики и советы

Чтобы интеграционное те

Блоги

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