Testen in C++: Frameworks und Tools
Die Codequalität in C++ ist ein kritischer Faktor für die Zuverlässigkeit von Software. Im Gegensatz zu Sprachen mit verwaltetem Speicher können Fehler in C++ zu schwerwiegenden Abstürzen, Speicherlecks und undefiniertem Verhalten führen. Ein System für Modultests (Unit-Tests) ermöglicht es, Defekte frühzeitig zu erkennen, erleichtert Refactoring und dokumentiert das Verhalten von Funktionen. In diesem Artikel betrachten wir drei beliebte Frameworks zum Testen in C++: Google Test, Catch2 und doctest sowie Tools für deren Integration in den Entwicklungsprozess.
Warum ist Testen in C++ notwendig?
- Sicherheit beim Refactoring: Tests signalisieren sofort Regressionen.
- Dokumentation: Lesbare Tests dienen als lebende Beispiele für die API-Nutzung.
- Integration mit CI: Automatischer Testlauf bei jedem Commit.
- Erkennung von undefiniertem Verhalten: Tests mit Sanitizern (AddressSanitizer, UndefinedBehaviorSanitizer).
1. Google Test (GTest)
Das beliebteste Framework von Google. Unterstützt xUnit-Stil, eine umfangreiche Sammlung von Assertions-Makros, automatische Testregistrierung, Mocks (über Google Mock) und parametrisierte Tests.
Installation (über CMake FetchContent)
# CMakeLists.txtinclude(FetchContent)FetchContent_Declare( googletest GIT_REPOSITORY https://github.com/google/googletest.git GIT_TAG release-1.12.1)FetchContent_MakeAvailable(googletest)
add_executable(my_tests test.cpp)target_link_libraries(my_tests GTest::gtest_main)Testbeispiel
#include <gtest/gtest.h>
int factorial(int n) { return (n <= 1) ? 1 : n * factorial(n - 1);}
TEST(FactorialTest, HandlesZeroInput) { EXPECT_EQ(factorial(0), 1);}
TEST(FactorialTest, HandlesPositiveInput) { EXPECT_EQ(factorial(5), 120); EXPECT_GT(factorial(3), 5);}
int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS();}TEST()— Makro zur Definition eines Testfalls.EXPECT_EQ— Gleichheitsprüfung (nicht-fatal).ASSERT_*— fatale Prüfungen (unterbrechen den Test bei Fehlschlag).
2. Catch2
Ein modernes Framework mit Fokus auf Lesbarkeit und Einfachheit. Unterstützt BDD-Stil (Given-When-Then), Sektionen (Aufteilung von Tests in verschachtelte Szenarien) und Generierung von Zufallsdaten. Erfordert keine Vererbung von Basisklassen.
Installation (Single-Header oder über vcpkg)
# vcpkgvcpkg install catch2
# CMakefind_package(Catch2 REQUIRED)target_link_libraries(my_tests Catch2::Catch2)Testbeispiel
#define CATCH_CONFIG_MAIN#include <catch2/catch.hpp>
int add(int a, int b) { return a + b; }
TEST_CASE("Addition works", "[math]") { REQUIRE(add(2, 2) == 4); CHECK(add(-1, 1) == 0);
SECTION("With zero") { REQUIRE(add(0, 5) == 5); }}
TEST_CASE("String concatenation", "[string]") { std::string result = std::string("Hello ") + "World"; CHECK(result == "Hello World");}TEST_CASE— Deklaration eines Tests mit Name und Tags.REQUIRE— fatale Prüfung.CHECK— nicht-fatale Prüfung.SECTION— verschachteltes Szenario (jede Sektion wird aus einem gemeinsamen Zustand gestartet).
3. Doctest
Eine leichte und schnelle Alternative. Inspiriert von Catch2, kompiliert aber 2-5 Mal schneller. Entwickelt für Projekte, bei denen die Build-Geschwindigkeit wichtig ist. Unterstützt Header-only, die Makros CHECK und REQUIRE sowie die Ausgabe im XML-Format.