Интеграционное тестирование C++ с Docker: продвинутый уровень

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

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

Начать курс

Интеграционное тестирование C++ с Docker: продвинутый уровень

Разработка современных C++ приложений всё чаще переходит в парадигму микросервисов и контейнеризации. Интеграционное тестирование (Integration Testing) становится критическим этапом, позволяющим проверить взаимодействие компонентов системы в условиях, максимально приближенных к боевым. Docker предоставляет идеальную среду для изолированного и воспроизводимого тестирования. В этой статье мы рассмотрим продвинутые техники интеграционного тестирования C++ с использованием Docker, включая настройку контейнеров, написание тестов с Google Test и интеграцию в пайплайны CI/CD.

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

Традиционное юнит-тестирование проверяет отдельные функции или классы, но не гарантирует корректной работы системы в целом. Интеграционные тесты проверяют связки: базы данных, сетевые протоколы, файловую систему, внешние API. Здесь Docker становится незаменимым:

  • Воспроизводимость: Docker-образ содержит точную копию окружения (компилятор, библиотеки, переменные окружения). Тест, пройденный локально, пройдёт на сервере CI и у другого разработчика.
  • Изоляция: Каждый тест запускается в чистом контейнере. Нет "загрязнения" глобальными настройками ОС или остаточными данными от предыдущих тестов.
  • Параллелизм: Можно запускать множество тестовых контейнеров одновременно, экономя время в CI/CD.
  • Управление зависимостями: Docker Compose позволяет поднять целую инфраструктуру: PostgreSQL, Redis, Kafka — за несколько секунд.

2. Настройка тестового окружения с Docker Compose

Рассмотрим типичный сценарий: C++ сервис, который читает данные из PostgreSQL и отправляет их в RabbitMQ. Нам нужно протестировать интеграцию этих трёх компонентов.

Создадим docker-compose.test.yml:

version: '3.8'
services:
  postgres_test:
    image: postgres:15-alpine
    environment:
      POSTGRES_DB: testdb
      POSTGRES_USER: testuser
      POSTGRES_PASSWORD: testpass
    ports:
      - "5432:5432"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U testuser -d testdb"]
      interval: 5s
      timeout: 5s
      retries: 5

  rabbitmq_test:
    image: rabbitmq:3-management-alpine
    environment:
      RABBITMQ_DEFAULT_USER: testuser
      RABBITMQ_DEFAULT_PASS: testpass
    ports:
      - "5672:5672"
    healthcheck:
      test: ["CMD", "rabbitmq-diagnostics", "check_port_connectivity"]
      interval: 5s
      timeout: 5s
      retries: 5

  test_runner:
    build:
      context: .
      dockerfile: Dockerfile.test
    depends_on:
      postgres_test:
        condition: service_healthy
      rabbitmq_test:
        condition: service_healthy
    environment:
      DB_HOST: postgres_test
      DB_PORT: 5432
      DB_NAME: testdb
      DB_USER: testuser
      DB_PASS: testpass
      RMQ_HOST: rabbitmq_test
      RMQ_PORT: 5672

Ключевой момент — healthcheck. Мы не запускаем тесты, пока база данных и брокер сообщений не будут готовы к работе. Это предотвращает "race condition" в тестах.

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

Для демонстрации используем Google Test (gtest) — стандарт де-факто для тестирования C++. Напишем тест, который проверяет вставку данных в PostgreSQL и их чтение через RabbitMQ.

Сначала создадим простую обёртку для работы с RabbitMQ (упрощённо):

// rabbitmq_client.h
#pragma once
#include <string>
#include <amqpcpp.h>

class RabbitMQClient {
public:
    RabbitMQClient(const std::string& host, int port,
                   const std::string& user, const std::string& pass);
    bool sendMessage(const std::string& queue, const std::string& message);
    std::string receiveMessage(const std::string& queue, int timeout_ms);
};

Теперь напишем интеграционный тест:

// integration_test.cpp
#include <gtest/gtest.h>
#include <pqxx/pqxx>  // libpqxx для PostgreSQL
#include "rabbitmq_client.h"

class IntegrationTest : public ::testing::Test {
protected:
    void SetUp() override {
        // Подключение к PostgreSQL
        conn = std::make_unique<pqxx::connection>(
            "host=" + db_host +
            " port=" + db_port +
            " dbname=" + db_name +
            " user=" + db_user +
            " password=" + db_pass
        );
        // Создаём тестовую таблицу
        pqxx::work txn(*conn);
        txn.exec("CREATE TABLE IF NOT EXISTS test_data ("
                 "id SERIAL PRIMARY KEY, "
                 "value TEXT NOT NULL)");
        txn.commit();

        // Подключение к RabbitMQ
        rmq = std::make_unique<RabbitMQClient>(
            rmq_host, std::stoi(rmq_port),
            rmq_user, rmq_pass
        );
    }

    void TearDown() override {
        // Очистка: удаляем таблицу и отключаемся
        pqxx::work txn(*conn);
        txn.exec("DROP TABLE IF EXISTS test_data");
        txn.commit();
        conn->close();
    }

    std::unique_ptr<pqxx::connection> conn;
    std::unique_ptr<RabbitMQClient> rmq;

    const std::string db_host = std::getenv("DB_HOST") ?: "localhost";
    const std::string db_port = std::getenv("DB_PORT") ?: "5432";
    const std::string db_name = std::getenv("DB_NAME") ?: "testdb";
    const std::string db_user = std::getenv("DB_USER") ?: "testuser";
    const std::string db_pass = std::getenv("DB_PASS") ?: "testpass";

    const std::string rmq_host = std::getenv("RMQ_HOST") ?: "localhost";
    const std::string rmq_port = std::getenv("RMQ_PORT") ?: "5672";
    const std::string rmq_user = std::getenv("RMQ_USER") ?: "testuser";
    const std::string rmq_pass = std::getenv("RMQ_PASS") ?: "testpass";
};

TEST_F(IntegrationTest, InsertAndPublish) {
    // Шаг 1: Вставляем данные в БД
    pqxx::work txn(*conn);
    txn.exec_params("INSERT INTO test_data (value) VALUES ($1)", "hello_docker");                

Блоги

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