Integration Testing C++ with Docker: Advanced Level
The development of modern C++ applications is increasingly moving towards the paradigm of microservices and containerization. Integration Testing becomes a critical stage, allowing verification of component interactions in conditions as close to production as possible. Docker provides an ideal environment for isolated and reproducible testing. In this article, we will explore advanced techniques for integration testing C++ using Docker, including container setup, writing tests with Google Test, and integration into CI/CD pipelines.
1. Why Docker is the Ideal Tool for C++ Integration Testing?
Traditional unit testing checks individual functions or classes but does not guarantee the correct operation of the system as a whole. Integration tests verify connections: databases, network protocols, file systems, external APIs. This is where Docker becomes indispensable:
- Reproducibility: A Docker image contains an exact copy of the environment (compiler, libraries, environment variables). A test that passes locally will pass on the CI server and for another developer.
- Isolation: Each test runs in a clean container. There is no "contamination" from global OS settings or residual data from previous tests.
- Parallelism: You can run multiple test containers simultaneously, saving time in CI/CD.
- Dependency Management: Docker Compose allows you to spin up an entire infrastructure: PostgreSQL, Redis, Kafka — in seconds.
2. Setting Up a Test Environment with Docker Compose
Let's consider a typical scenario: a C++ service that reads data from PostgreSQL and sends it to RabbitMQ. We need to test the integration of these three components.
Let's create 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: 5672The key point is healthcheck. We do not run tests until the database and message broker are ready to operate. This prevents "race conditions" in tests.
3. Writing Integration Tests in C++ with Google Test
For demonstration, we will use Google Test (gtest) — the de facto standard for C++ testing. Let's write a test that verifies inserting data into PostgreSQL and reading it via RabbitMQ.
First, let's create a simple wrapper for working with RabbitMQ (simplified):
// 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