Tests en C++ : frameworks et outils
La qualité du code en C++ est un facteur critique pour la fiabilité des logiciels. Contrairement aux langages à mémoire gérée, les erreurs en C++ peuvent entraîner des pannes graves, des fuites mémoire et un comportement indéfini. Le système de tests modulaires (tests unitaires) permet de détecter les défauts dès les premières étapes, facilite le refactoring et documente le comportement des fonctions. Dans cet article, nous examinerons trois frameworks populaires pour les tests en C++ : Google Test, Catch2 et doctest, ainsi que les outils pour leur intégration dans le processus de développement.
Pourquoi les tests en C++ sont-ils nécessaires ?
- Sécurité lors du refactoring : les tests signalent instantanément les régressions.
- Documentation : des tests lisibles servent d'exemples vivants d'utilisation de l'API.
- Intégration avec CI : exécution automatique des tests à chaque commit.
- Détection des comportements indéfinis : tests avec des sanitizers (AddressSanitizer, UndefinedBehaviorSanitizer).
1. Google Test (GTest)
Le framework le plus populaire de Google. Il prend en charge le style xUnit, un riche ensemble de macros d'assertions, l'enregistrement automatique des tests, les mocks (via Google Mock) et les tests paramétrés.
Installation (via 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)Exemple de test
#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()— macro pour définir un cas de test.EXPECT_EQ— vérification d'égalité (non fatale).ASSERT_*— vérifications fatales (interrompent le test en cas d'échec).
2. Catch2
Un framework moderne axé sur la lisibilité et la simplicité. Il prend en charge le style BDD (Given-When-Then), les sections (division des tests en scénarios imbriqués) et la génération de données aléatoires. Il ne nécessite pas d'héritage de classes de base.
Installation (single-header ou via vcpkg)
# vcpkgvcpkg install catch2
# CMakefind_package(Catch2 REQUIRED)target_link_libraries(my_tests Catch2::Catch2)Exemple de test
#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— déclaration d'un test avec un nom et des tags.REQUIRE— vérification fatale.CHECK— vérification non fatale.SECTION— scénario imbriqué (chaque section s'exécute à partir d'un état commun).
3. Doctest
Une alternative légère et rapide. Inspiré de Catch2, mais compile 2 à 5 fois plus vite. Conçu pour les projets où la vitesse de compilation est importante. Prend en charge le fichier d'en-tête (header-only), les macros CHECK et REQUIRE, ainsi que la sortie au format XML.