Testen in C++: Unit-, Integrations- und E2E-Tests mit Beispielen

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

Einleitung: Warum Testen für C++-Projekte kritisch ist



Das Testen von C++-Code ist keine bloße Formalität, sondern eine grundlegende Praxis, die Stabilität, Sicherheit und Wartbarkeit von Software gewährleistet. Im Gegensatz zu interpretierten Sprachen, bei denen ein Fehler sofort auftreten kann, bleiben Bugs in C++ oft bis zum Build oder zur Ausführung unter spezifischen Bedingungen verborgen. Eine übersehene Speicherleckage, undefiniertes Verhalten oder eine fehlerhafte Zeigerverwaltung können zu fatalen Abstürzen führen.



Die moderne Softwareentwicklungsindustrie unterscheidet drei Haupttestebenen: Unit (Modultests), Integration (Integrationstests) und E2E (End-to-End-Tests). Jede Ebene erfüllt ihre eigene Aufgabe und zusammen bilden sie die "Testpyramide", mit der bis zu 90% der Fehler vor dem Produktionseinsatz gefunden werden können.



In diesem Artikel analysieren wir jeden Testtyp anhand praktischer C++-Beispiele unter Verwendung der populären Frameworks Google Test und Catch2 und diskutieren die besten Praktiken für deren Anwendung.



1. Unit-Testing: Überprüfung atomarer Module



Unit-Tests sind die unterste und schnellste Ebene. Sie überprüfen einzelne Funktionen, Klassen oder Methoden isoliert vom Rest des Systems. Das Ziel ist sicherzustellen, dass jeder "Baustein" des Codes korrekt funktioniert.



Beispiel eines Unit-Tests mit Google Test



Betrachten wir eine einfache mathematische Funktion:



// math_utils.h#pragma once

namespace MathUtils { int add(int a, int b); int divide(int a, int b);}


// math_utils.cpp#include "math_utils.h"

namespace MathUtils { int add(int a, int b) { return a + b; }

int divide(int a, int b) { if (b == 0) { throw std::invalid_argument("Division by zero"); } return a / b; }}


Jetzt schreiben wir Unit-Tests mit Google Test:



// test_math_utils.cpp#include <gtest/gtest.h>#include "math_utils.h"

TEST(MathUtilsTest, AddPositiveNumbers) { EXPECT_EQ(MathUtils::add(2, 3), 5); EXPECT_EQ(MathUtils::add(0, 0), 0);}

TEST(MathUtilsTest, AddNegativeNumbers) { EXPECT_EQ(MathUtils::add(-1, -1), -2); EXPECT_EQ(MathUtils::add(-5, 3), -2);}

TEST(MathUtilsTest, DivideNormal) { EXPECT_EQ(MathUtils::divide(10, 2), 5); EXPECT_EQ(MathUtils::divide(7, 3), 2); // Integer division}

TEST(MathUtilsTest, DivideByZeroThrows) { EXPECT_THROW(MathUtils::divide(5, 0), std::invalid_argument);}


Verwendung von Mock-Objekten



Zur Isolation eines Moduls ist es oft notwendig, Abhängigkeiten zu ersetzen. Google Mock (Teil von Google Test) ermöglicht das Erstellen von Platzhaltern:



// database_interface.hclass DatabaseInterface {public:    virtual ~DatabaseInterface() = default;    virtual bool save(const std::string& data) = 0;};

// user_service.cppclass UserService { DatabaseInterface& db;public: explicit UserService(DatabaseInterface& db) : db(db) {} bool registerUser(const std::string& name) { return db.save("user:" + name); }};

// test_user_service.cpp#include <gmock/gmock.h>

class MockDatabase : public DatabaseInterface {public: MOCK_METHOD(bool, save, (const std::string&), (override));};

TEST(UserServiceTest, RegisterUserCallsDb) { MockDatabase mockDb; EXPECT_CALL(mockDb, save("user:Alice")).WillOnce(testing::Return(true));

UserService service(mockDb); EXPECT_TRUE(service.registerUser("Alice"));}


Vorteile: Ausführungsgeschwindigkeit (Millisekunden), präzise Fehlerlokalisierung, Möglichkeit zum Ausführen ohne externe Abhängigkeiten.
Nachteile: Sie überprüfen nicht die Interaktion zwischen realen Komponenten.



2. Integrationstesting: Überprüfung des Komponentenverbunds



Integrationstests überprüfen, wie mehrere Mo

Blogs

Buchempfehlungen