Интеграционное тестирование C++ с Docker
Современная разработка на C++ требует не только написания качественного кода, но и обеспечения его надёжной работы в реальных условиях. Интеграционное тестирование — ключевой этап, позволяющий проверить взаимодействие компонентов системы: базы данных, сетевых сервисов, файловой системы. Использование Docker для этих целей даёт изолированное, воспроизводимое окружение, которое идеально подходит для CI/CD пайплайнов. В этой статье мы разберём, как настроить интеграционное тестирование C++ приложений с помощью Docker, и приведём практические примеры.
Почему Docker для интеграционного тестирования C++?
Традиционные подходы к интеграционному тестированию C++ часто сталкиваются с проблемами:
- Зависимость от окружения: тесты могут работать на машине разработчика, но падать на сервере сборки из-за разных версий библиотек.
- Сложность настройки: для тестирования с реальной базой данных или внешним API требуется ручная конфигурация.
- Параллельный запуск: несколько тестовых прогонов могут конфликтовать из-за общих ресурсов.
Docker решает эти проблемы, предоставляя лёгкие, изолированные контейнеры. Вы можете поднять тестовую базу данных, запустить ваш C++ сервис и выполнить тесты — всё в рамках одного docker-compose файла. Это гарантирует, что окружение будет идентичным на любой машине.
Настройка проекта: базовый пример
Рассмотрим простой C++ сервис, который сохраняет данные в PostgreSQL. Для интеграционного тестирования нам понадобится:
- Сам C++ код с тестами (используем Google Test).
Dockerfileдля сборки и запуска тестов.docker-compose.ymlдля оркестрации контейнеров.
Структура проекта:
my_cpp_project/ ├── src/ │ ├── main.cpp │ ├── database.cpp │ └── database.h ├── tests/ │ └── test_integration.cpp ├── CMakeLists.txt ├── Dockerfile └── docker-compose.yml
Dockerfile:
FROM gcc:latest AS builder
WORKDIR /app
COPY . .
RUN apt-get update && apt-get install -y cmake libpq-dev
RUN cmake -B build -DCMAKE_BUILD_TYPE=Release
RUN cmake --build build
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y libpq-dev
COPY --from=builder /app/build/tests /app/tests
COPY --from=builder /app/build/my_app /app/my_app
ENTRYPOINT ["/app/tests"]
docker-compose.yml:
version: '3.8'
services:
db:
image: postgres:15
environment:
POSTGRES_USER: testuser
POSTGRES_PASSWORD: testpass
POSTGRES_DB: testdb
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U testuser -d testdb"]
interval: 5s
timeout: 5s
retries: 5
test:
build: .
depends_on:
db:
condition: service_healthy
environment:
DB_HOST: db
DB_PORT: 5432
DB_USER: testuser
DB_PASSWORD: testpass
DB_NAME: testdb
Написание интеграционных тестов на C++
Теперь создадим тест, который проверяет запись и чтение из базы данных. Используем Google Test и библиотеку libpqxx для работы с PostgreSQL.
tests/test_integration.cpp:
#include <gtest/gtest.h>
#include <pqxx/pqxx>
#include <string>
class DatabaseTest : public ::testing::Test {
protected:
void SetUp() override {
conn = new pqxx::connection(
"host=" + std::string(std::getenv("DB_HOST")) +
" port=" + std::string(std::getenv("DB_PORT")) +
" user=" + std::string(std::getenv("DB_USER")) +
" password=" + std::string(std::getenv("DB_PASSWORD")) +
" dbname=" + std::string(std::getenv("DB_NAME"))
);
// Создаём таблицу для теста
pqxx::work txn(*conn);
txn.exec("CREATE TABLE IF NOT EXISTS test_data (id SERIAL PRIMARY KEY, value TEXT)");
txn.commit();
}
void TearDown() override {
pqxx::work txn(*conn);
txn.exec("DROP TABLE IF EXISTS test_data");
txn.commit();
delete conn;
}
pqxx::connection* conn;
};
TEST_F(DatabaseTest, InsertAndRetrieve) {
pqxx::work txn(*conn);
txn.exec("INSERT INTO test_data (value) VALUES ('hello')");
txn.commit();
pqxx::work txn2(*conn);
pqxx::result res = txn2.exec("SELECT value FROM test_data");
EXPECT_EQ(res.size(), 1);
EXPECT_EQ(res[0][0].as<std::string>(), "hello");
txn2.commit();
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
CMakeLists.txt:
cmake_minimum_required(VERSION 3.10)
project(MyCppProject)
find_package(PkgConfig REQUIRED)
pkg_check_modules(PQXX REQUIRED libpqxx)
pkg_check_modules(PQ REQUIRED libpq)
add_executable(tests tests/test_integration.cpp)
target_link_libraries(tests PRIVATE ${PQXX_LIBRARIES} ${PQ_LIBRARIES} gtest gtest_main)
target_include_directories(tests PRIVATE ${PQXX_INCLUDE_DIRS} ${PQ_INCLUDE_DIRS})
Интеграция с CI/CD (GitHub Actions)
Чтобы автоматизировать запуск тестов при каждом коммите, добавим workflow для GitHub Actions.
.github/workflows/test.yml:
name: Integration Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run integration tests
run: |
docker-compose -f docker-compose.yml up --build --abort-on-container-exit
- name: Cleanup
if: always()
run: docker-compose down
Этот workflow поднимает контейнеры, ждёт готовности базы данных (благодаря healthcheck), запускает тесты и завершает работу. Флаг --abort-on-container-exit гарантирует, что пайплайн остановится при ошибке тестов.