Integrationstests für C++ mit Docker
Moderne C++-Entwicklung erfordert nicht nur das Schreiben von qualitativ hochwertigem Code, sondern auch die Sicherstellung seines zuverlässigen Betriebs unter realen Bedingungen. Integrationstests sind eine Schlüsselphase, die es ermöglicht, die Interaktion von Systemkomponenten zu überprüfen: Datenbanken, Netzwerkdienste, Dateisysteme. Die Verwendung von Docker für diese Zwecke bietet eine isolierte, reproduzierbare Umgebung, die sich ideal für CI/CD-Pipelines eignet. In diesem Artikel werden wir durchgehen, wie man Integrationstests für C++-Anwendungen mit Docker einrichtet, und praktische Beispiele liefern.
Warum Docker für C++-Integrationstests?
Traditionelle Ansätze für C++-Integrationstests stoßen oft auf Probleme:
- Umgebungsabhängigkeit: Tests können auf dem Entwicklerrechner funktionieren, aber auf dem Build-Server aufgrund unterschiedlicher Bibliotheksversionen fehlschlagen.
- Komplexität der Einrichtung: Für Tests mit einer echten Datenbank oder einer externen API ist eine manuelle Konfiguration erforderlich.
- Paralleler Start: Mehrere Testläufe können aufgrund gemeinsamer Ressourcen in Konflikt geraten.
Docker löst diese Probleme, indem es leichte, isolierte Container bereitstellt. Sie können eine Testdatenbank hochfahren, Ihren C++-Dienst starten und die Tests ausführen – alles innerhalb einer einzigen docker-compose-Datei. Dies garantiert, dass die Umgebung auf jeder Maschine identisch ist.
Projekteinrichtung: Ein Basisbeispiel
Betrachten wir einen einfachen C++-Dienst, der Daten in PostgreSQL speichert. Für Integrationstests benötigen wir:
- Den C++-Code selbst mit Tests (wir verwenden Google Test).
- Ein
Dockerfilezum Bauen und Ausführen der Tests. - Eine
docker-compose.ymlzur Orchestrierung der Container.
Projektstruktur:
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 /appCOPY . .
RUN apt-get update && apt-get install -y cmake libpq-devRUN cmake -B build -DCMAKE_BUILD_TYPE=ReleaseRUN cmake --build build
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y libpq-devCOPY --from=builder /app/build/tests /app/testsCOPY --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: testdbSchreiben von C++-Integrationstests
Jetzt erstellen wir einen Test, der das Schreiben und Lesen aus der Datenbank überprüft. Wir verwenden Google Test und die Bibliothek libpqxx für die Arbeit mit 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")) ); }
void TearDown() override { delete conn; }
pqxx::connection *conn;};
TEST_F(DatabaseTest, CanInsertAndRetrieveData) { pqxx::work txn(*conn); txn.exec("CREATE TABLE IF NOT EXISTS test_table (id SERIAL PRIMARY KEY, value TEXT)"); txn.exec("INSERT INTO test_table (value) VALUES ('Hello, Docker!')"); txn.commit();
pqxx::work txn2(*conn); pqxx::result result = txn2.exec("SELECT value FROM test_table WHERE value = 'Hello, Docker!'"); EXPECT_EQ(result.size(), 1); EXPECT_EQ(result[0][0].as(), "Hello, Docker!");}