Testen in C++: Unit, Integration, E2E – Fortgeschrittenes Niveau

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Fortgeschrittenes Testen von C++: Von Unit-Tests bis End-to-End

In der modernen C++-Entwicklung wird die Codequalität nicht nur durch seine Leistung, sondern auch durch seine Zuverlässigkeit bestimmt. Fortgeschrittenes Testen geht weit über einfache Überprüfungen hinaus – es ist ein architektonischer Ansatz, der Unit-Tests, Integrationstests und End-to-End (E2E)-Tests umfasst. In diesem Artikel analysieren wir, wie man die Testpyramide für C++-Projekte richtig aufbaut, unter Verwendung von Google Test, Google Mock und eigenen Frameworks.



1. Unit-Testing: Isolation und Mocks

Unit-Tests überprüfen einzelne Module (Funktionen, Klassen) in Isolation. Auf fortgeschrittenem Niveau besteht die Hauptaufgabe darin, zu lernen, Abhängigkeiten richtig zu mocken. Betrachten wir ein Beispiel mit Google Mock.



Angenommen, wir haben eine Klasse DatabaseConnector, die von einer externen API abhängt. Wir möchten die Datenverarbeitungslogik testen, ohne auf die echte Datenbank zuzugreifen.



#include <gmock/gmock.h>#include <gtest/gtest.h>

class DatabaseInterface {public: virtual ~DatabaseInterface() = default; virtual std::string fetchData(int id) = 0;};

class MockDatabase : public DatabaseInterface {public: MOCK_METHOD(std::string, fetchData, (int id), (override));};

class DataProcessor { DatabaseInterface& db;public: explicit DataProcessor(DatabaseInterface& db) : db(db) {} std::string process(int id) { auto raw = db.fetchData(id); if (raw.empty()) return "EMPTY"; return "PROCESSED: " + raw; }};

TEST(DataProcessorTest, ProcessWithValidData) { MockDatabase mockDb; EXPECT_CALL(mockDb, fetchData(42)) .WillOnce(testing::Return("Hello")); DataProcessor processor(mockDb); EXPECT_EQ(processor.process(42), "PROCESSED: Hello");}


Die Verwendung von MOCK_METHOD ermöglicht eine präzise Kontrolle des Verhaltens von Abhängigkeiten. Es ist wichtig, an strikte Mocks (über NiceMock oder StrictMock) zu denken, um unerwartete Aufrufe abzufangen.



2. Integrationstests: Testen von Interaktionen

Integrationstests überprüfen, wie Module zusammenarbeiten. In C++ umfasst dies oft das Testen der Arbeit mit dem Dateisystem, Netzwerkprotokollen oder Datenbanken. Ein fortgeschrittener Ansatz ist die Verwendung von Test Fixtures und parametrisierten Tests.



Beispiel: Wir testen die Klasse FileManager, die Dateien liest und schreibt. Wir verwenden temporäre Verzeichnisse.



class FileManagerTest : public ::testing::Test {protected:    std::filesystem::path tempDir;    void SetUp() override {        tempDir = std::filesystem::temp_directory_path() / "test_files";        std::filesystem::create_directories(tempDir);    }    void TearDown() override {        std::filesystem::remove_all(tempDir);    }};

TEST_F(FileManagerTest, WriteAndReadFile) { FileManager fm(tempDir.string()); fm.write("test.txt", "data"); auto content = fm.read("test.txt"); EXPECT_EQ(content, "data");}


Für parametrisierte Tests verwenden wir TEST_P mit INSTANTIATE_TEST_SUITE_P. Dies ermöglicht es, denselben Test mit verschiedenen Eingabedaten auszuführen.



class MathTest : public ::testing::TestWithParam<std::tuple<int, int, int>> {};TEST_P(MathTest, Add) {    auto [a, b, expected] = GetParam();    EXPECT_EQ(add(a, b), expected);}INSTANTIATE_TEST_SUITE_P(AddTests, MathTest,    ::testing::Values(        std::make_tuple(1, 1, 2),        std::make_tuple(-1, 1, 0),        std::make_tuple(0, 0, 0)    ));


3. End-to-End-Tests: Überprüfung des vollständigen Zyklus

E2E-Tests simulieren das Verhalten des Endbenutzers. Für C++-Anwendungen kann dies bedeuten, die vollständige Binärdatei auszuführen, ihre Ausgabe zu überprüfen oder über die API zu interagieren. Fortgeschrittenes Niveau

Blogs

Buchempfehlungen