Parmi les Frameworks de tests unitaires, Boost Unit Test Framework est l'un des plus complets.
En effet, basé sur l'architecture xUnit, il permet à la fois les fixtures, les exceptions, les templates et permet de grouper les tests en différentes suites. Bref, à la fois flexible et facile à utiliser, c'est un outil idéal pour l'écriture de tests unitaires en C++.
Comment utiliser Boost Unit Test Framework
1. La compilation
Boost Unit Test Framework peut être linké soit de manière dynamique soit de manière statique.
Dans tous les cas, il vous suffit d'installer Boost Test (les distributions debian-like fournissent le packet libboost-test-dev). Puis, pour le compiler avec votre projet, de le linker en ajoutant le flag -lboost_unit_test_framework.
Pour compiler avec Boost Test Library de manière dynamique, vous aurez aussi besoin de définir BOOST_TEST_DYN_LINK dans votre code. Comme ceci :
#define BOOST_TEST_DYN_LINK
2. L'écriture des tests
Un test se présente comme ceci :
BOOST_AUTO_TEST_SUITE (teststring) // le nom de la série de test est teststring BOOST_AUTO_TEST_CASE (test1) // premier test unitaire { mystring s; BOOST_CHECK(s.size() == 0); // test basique } BOOST_AUTO_TEST_CASE (test2) // deuxième test unitaire { mystring s; s.setbuffer("hello world"); BOOST_REQUIRE_EQUAL ('h', s[0]); } BOOST_AUTO_TEST_SUITE_END( )
Les macros BOOST_AUTO_TEST_SUITE et BOOST_AUTO_TEST_SUITE_END indiquent le début et la fin de la série de tests, elles délimitent l'équivalent d'un namespace en C++.
Entre ces deux macros, on écrit les différents tests unitaires avec la macro BOOST_AUTO_TEST_CASE en spécifiant le nom du test à chaque fois.
BOOST_CHECK et BOOST_REQUIRE_EQUAL sont deux des nombreuses macros prédéfinies par boost pour tester les valeurs des variables.
Boost permet de lever plusieurs niveaux d'erreurs lors de l'exécution d'une série de test :
Si une condition de BOOST_REQUIRE n'est pas validée, alors la suite des tests n'est pas exécutée. Par contre, si BOOST_CHECK échoue, les tests suivants sont quand même exécutés. Boost permet aussi de lever des erreurs critiques.
Tout ceci permet à la suite de tests de s'adapter à de nombreux usages, que ce soit des tests en cours de développement ou des tests de non régression.
3. Vers des tests plus avancés
L'une des forces du Framework de test de boost est de permettre de faire des tests plus avancés tels que le pattern matching ou de la comparaison de flottants.
Boost permet aussi de construire de nouveaux prédicats :
boost::test_tools::predicate_result validate_list(std::list<int>& L1) { std::list<int>::iterator it1 = L1.begin( ); for (; it1 != L1.end( ); ++it1) { if (*it1 <= 1) return false; } return true; } BOOST_AUTO_TEST_SUITE ( test ) BOOST_AUTO_TEST_CASE( test ) { std::list<int>& list1 = user_defined_func( ); BOOST_CHECK( validate_list(list1) ); } BOOST_AUTO_TEST_SUITE_END( )
Comme on peut le voir dans l'exemple précédents, vous pouvez écrire vos propres prédicats. Ce qui facilite grandement l'écriture des tests, notamment dans le cas des ensembles complexes ou dans le cas de classes que vous avez créées vous-même.
4. Les fixtures
Une fixture permet de créer un environnement avant que le test soit exécuté. Cet environnement sera détruit une fois que le test est terminé.
On utilise BOOST_FIXTURE_TEST_CASE qui prend en argument un objet. Il suffit de définir l'objet qui crée tous les éléments nécessaires au test et qui les détruit à la fin du test.
#define BOOST_TEST_MODULE example
#include <boost/test/included/unit_test.hpp>
struct F {
F() : i( 0 ) { BOOST_TEST_MESSAGE( "setup fixture" ); }
~F() { BOOST_TEST_MESSAGE( "teardown fixture" ); }
int i;
};
BOOST_FIXTURE_TEST_CASE( test_case1, F )
{
BOOST_CHECK( i == 1 );
++i;
}
BOOST_FIXTURE_TEST_CASE( test_case2, F )
{
BOOST_CHECK_EQUAL( i, 1 );
}
BOOST_AUTO_TEST_CASE( test_case3 )
{
BOOST_CHECK( true );
}
5. Les résultats
Si vous ne définissez pas vous même de programme principal afin de lancer vos tests unitaires, Boost en génère un pour vous ! Du coup, exécuter vos tests est un véritable jeu d'enfant.
Voici un exemple :
#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE Hello
int add(int i, int j)
{
return i + j;
}
BOOST_AUTO_TEST_CASE(universeInOrder)
{
BOOST_CHECK(add(2, 2) == 4);
}
$ g++ -ohello -lboost_unit_test_framework hello.cpp
$ ./hello --log_level=test_suite
Running 1 test case...
Entering test case "Hello"
Leaving test case "Hello"
*** No errors detected
Boost Test permet de générer des rapports au format XML. Ce format permet notamment de faire du reporting dans un serveur d'intégration continue.
Pour générer votre XML à partir de votre suite de tests compilée, c'est extrêmement simple. Il suffit d'exécuter votre programme de tests avec l'option --log_format=XML.
De plus, vous pouvez générer des XML plus ou moins précis en fonction de vos besoins. Pour choisir votre niveau de logs vous pouvez utiliser l'option --log_level. Vous pouvez notamment choisir la valeur test_suite qui vous permet d'avoir les messages de la suite de tests, ce qui est très pratique pour investiguer les résultats de tests en échec.
Le résultat devrait ressembler à ceci :
<TestLog>
<TestSuite name="PulseTest">
<TestSuite name="VariantsSuite">
<TestCase name="simplePass">
<TestingTime>0</TestingTime>
</TestCase>
<TestCase name="checkFailure">
<Error file="main.cpp" line="20">check add(2, 2) == 5 failed</Error>
<TestingTime>0</TestingTime>
</TestCase>
...
</TestSuite>
</TestSuite>
</TestLog>
Voilà ! Vous avez tous les concepts de base pour vous servir du Framework de tests unitaires de Boost. Plus d'excuses pour ne pas vous en servir pour tester vos programmes.
Bibliographie
- http://www.boost.org/doc/libs/1_43_0/libs/test/doc/html/utf.html
- http://www.boost.org/doc/libs/1_43_0/libs/test/doc/html/tutorials/intro-in-testing.html
- http://www.boost.org/doc/libs/1_34_1/libs/test/doc/component/utf/index.html
- http://www.ibm.com/developerworks/aix/library/au-ctools1_boost/
- http://alexott.net/en/cpp/CppTestingIntro.html
Article très intéressant.
Petite remarque : un "include" a disparu (sûrement considéré comme balise html) dans le listing du paragraphe 4 "Les Fixtures".