Integration Testing C++ with Docker: Advanced Level
\n\nIntegration testing is a critically important stage of C++ development, especially in modern microservice architectures. Traditional approaches, where tests are run in the developer's environment, often lead to errors due to differences in environment, library versions, and configurations. Docker offers an elegant solution: complete isolation, reproducibility, and automation. In this article, we will explore advanced techniques for integration testing of C++ applications using Docker, including dependency management, parallel test execution, and CI/CD integration.
\n\n1. Test Environment Architecture on Docker
\n\nThe first step towards reliable integration testing is a proper Docker environment architecture. Instead of a single monolithic container, we create a composition of services, each responsible for its own task: the service under test itself, the database, cache, message queues, etc.
\n\nConsider an example for a C++ microservice that uses PostgreSQL and Redis. The file docker-compose.test.yml:
version: '3.8'\n\nservices:\n postgres_test:\n image: postgres:15-alpine\n environment:\n POSTGRES_USER: test_user\n POSTGRES_PASSWORD: test_pass\n POSTGRES_DB: test_db\n healthcheck:\n test: ["CMD-SHELL", "pg_isready -U test_user -d test_db"]\n interval: 2s\n timeout: 5s\n retries: 10\n\n redis_test:\n image: redis:7-alpine\n healthcheck:\n test: ["CMD", "redis-cli", "ping"]\n interval: 2s\n timeout: 5s\n retries: 10\n\n cpp_service_test:\n build:\n context: .\n dockerfile: Dockerfile.test\n depends_on:\n postgres_test:\n condition: service_healthy\n redis_test:\n condition: service_healthy\n environment:\n - DB_HOST=postgres_test\n - DB_PORT=5432\n - REDIS_HOST=redis_test\n - REDIS_PORT=6379\n - TEST_MODE=1\n volumes:\n - ./test_results:/app/test_results\n command: ["./run_integration_tests"]\n\nKey points:
\n- \n
- Healthchecks ensure that tests only start after dependencies are ready. \n
- Separate network (created automatically) isolates the test environment from production. \n
- Volume for results allows you to retrieve test reports after completion. \n
2. Writing Integration Tests in C++ with Docker in Mind
\n\nYour tests should be ready to work in a containerized environment. This means that connection configuration to external services should be set via environment variables, not hardcoded.
\n\nExample of a test using Google Test and libpqxx for PostgreSQL:
\n\n#include <gtest/gtest.h>\n#include <pqxx/pqxx>\n#include <cstdlib>\n\nclass DatabaseIntegrationTest : public ::testing::Test {\nprotected:\n std::unique_ptr<pqxx::connection> conn;\n\n void SetUp() override {\n const char* db_host = std::getenv("DB_HOST");\n const char* db_port = std::getenv("DB_PORT");\n const char* db_user = std::getenv("POSTGRES_USER");\n const char* db_pass = std::getenv("POSTGRES_PASSWORD");\n const char* db_name = std::getenv("POSTGRES_DB");\n\n ASSERT_NE(db_host, nullptr) << "DB_HOST not set";\n ASSERT_NE(db_port, nullptr) << "DB_PORT not set";\n\n std::string conn_str = "host=" + std::string(db_host) +\n " port=" + std::string(db_port) +\n " user=" + std::string(db_user ? db_user : "test_user") +\n " password=" + std::string(db_pass ? db_pass : "test_pass") +\n " dbname=" + std::string(db_name ? db_name : "test_db");\n\n try {\n conn = std::make_unique<pqxx::connect