Mokona Guu Center

Frameworks de tests unitaires en C++

Publié le

Il y a peu, je voulais faire un petit tour de ce qui existait en framework de tests unitaires en C++ avec certains critères. Un des critères étant l'accessibilité aux sources, je suis parti de cette liste.

Pour un premier tri, j'ai enlevé tout ce qui ne tournait que sur une seule plateforme. Ensuite, j'ai testé chaque framework restant pour vérifier que :

  • sa sortie était claire ;
  • il n'était pas trop compliqué ni lent à compiler/linker et s'exécuter ;
  • facile à installer ou même à transporter éventuellement avec le projet qu'il teste ;
  • gère les fixtures, voire une globale fixture.

Dans ce cadre j'ai testé Boost Test (lien mort), CppTest, CppUnit, CxxTest (lien mort), TUT, Unit--, UnitTest++.

Ce dont on se rend compte assez vite, c'est que beaucoup de ces frameworks se ressemblent, chacun apportant sa petite originalité par rapport aux autres. Ce n'est pas vraiment étonnant, un framework de test unitaire s'occupe d'un nombre réduit de choses : tester des états, attraper les exceptions, afficher les résultats. À cela s'ajoute des concepts de « suites » de tests, par thèmes, ou encore de « fixtures », permettant de mettre le test dans une condition précise et de remettre le système dans l'état dans lequel il était à la fin du test.

Boost Test

  • Version utilisée : 1.34.1
  • Licence : Boost

Je l'avais utilisé précédemment, j'ai donc commencé par celui-ci. Je l'avais trouvé un peu lourd et c'est une des raisons qui m'ont fait faire ce petit tour d'horizon.

Soucis majeur : la documentation. Boost offre une documentation en ligne pas toujours très à jour, et la documentation téléchargeable ne contient pas tous les modules. C'est à mon avis une grosse faiblesse de Boost dans son ensemble, que j'utilise par ailleurs car offrant des fonctionnalités pratiques. Heureusement, les sources disponibles permettent d'aller voir ce que la documentation ne dit pas, au risque d'utiliser de travers.

Dans le cas de Boost Test, la documentation disponible est celle de la 1.33. Malheureusement, entre la 1.33 et la 1.34, il y a eu pas mal de modifications. De plus, la documentation en ligne, au moment où je faisais mes tests, avait quelques soucis qui m'empêchaient d'aller consulter certaines pages. D'un point de vu mise en oeuvre, Boost Test est donc un fiasco. Une documentation à jour aurait probablement tout changé.

Boost Test est large : il offre beaucoup de fonctionnalités et on peut donc utiliser le framework de façons différentes. J'ai donc fait trois tests. Le premier, « minimal », utilise un système d'assert monitorés. C'est le système classique dans la plupart des frameworks. C'est simple, ça marche, mais c'est passer à côté d'une bonne partie du framework. Les appels aux tests doivent être manuel, entre autre.

Passons donc à un niveau supérieur, avec de l'enregistrement automatique de tests. La première façon de faire, qui était je l'ai compris après, l'ancienne façon de faire, est d'inclure tout le framework. Aucun soucis de fonctionnement, des macros servent à déclarer les tests qui sont ensuite appelés automatiquement. L'ennui, c'est le temps de compilation : 5 à 6 fois plus lent qu'une version de framework avec laquelle on link son test. Normal, ici, il faut tout recompiler. Cette méthode est donc exclue.

J'essaie donc la méthode avec enregistrements automatiques, mais avec link. J'y ai passé un peu de temps. En effet, la documentation indique des choses qui ne fonctionnent pas. Après un petit tour sur les archives de la mailing list, j'ai vu que je n'étais pas le seul et que ce système est une des parties qui a complètement était remise à plat entre la 1.33 et la 1.34. La documentation est donc fausse.

Autres constatations : Boost garde pour lui l'appel à « main », cela peut être gênant pour des initialisations globales et les « iostream » sont détournées. Boost attrape bien les exceptions non spécifiquement testées.

Au final, Boost Test me laisse un résultat amer. Cette documentation inaccessible en partie et fausse sur certains points m'a fait perdre du temps. Il reste cependant utilisable et est certainement plus puissant que ce que je n'ai vu dans mes tests.

Petite modification de janvier 2009 après test d'une version plus récente : beaucoup de progrès ont été fait. Tant sur la mise en place que sur la documentation (qui reste un peu touffue et pas forcément très facile à naviguer). J'ai pu mettre boost::test en place sur un projet de manière plutôt satisfaisante.

CppTest

  • Version utilisée : 1.0.2
  • Licence : GPL

CppTest a pour lui une documentation limpide. Mettre en place une batterie de tests est simple. L'installation se fait avec les autotools pour un environnent qui les supporte, ou grâce à un .sln pour Visual Studio.

Cependant, l'insertion de tests passe par une écriture dans une classe qui complexifie un peu l'écriture. La sortie de base n'est pas très claire mais peut-être modifiée. Étrangement, alors qu'une sortie HTML est disponible, une sortie XML n'est pas disponible, même si on peut l'ajouter.

Un framework plutôt sympathique donc, malheureusement, la version que j'ai testée plante si une exception est levée hors d'un test d'exception.

CppUnit

  • Version utilisée : 1.12
  • Licence : LGPL

CppUnit ressemble à CppTest, en plus complet et un peu plus complexe aussi. Heureusement, il est soutenu par une excellente documentation qui, même si elle n'est pas tout à fait à jour, fonctionne. La sortie est claire, configurable et des \"listeners\" sont disponibles pour ajouter ses fonctionnalités.

Comme CppUnit, les suites de tests sont gérées par l'écriture de classe. Cependant, CppUnit offre des macros qui permettent une écriture rapide de la déclaration des tests.

Deux bémols : l'ASSERT_THROW me laisse un warning à propos de l'exception que je lance et les entêtes à inclure sont un peu nombreuses.

À noter que CppUnit est accompagné d'outils pour les lancements de batteries de tests et leur monitoring.

CxxTest

  • Version utilisée : 3.10.1
  • Licence : LGPL

D'un point de vu écriture de tests, CxxTest reste dans la même famille que CppTest ou CppUnit. Là où il diffère complètement, c'est que CxxTest utilise une phase de pré-processing du code de test pour générer le code additionnel. Le résultat du pré-processing est à son tour compilé pour lancer le test.

CxxTest est très malléable, il s'utilise simplement, mais cette histoire de pré-processing me fait un peu peur. Le script de pré-processing est disponible en Perl ou en Python. Ce soucis d'offrir deux versions montre bien le problème : il est nécessaire d'avoir l'un ou l'autre sur la plateforme de développement. La multiplication des outils peut être un handicap, ce n'est pas a négliger. La durée du test est aussi un peu plus longue car en deux phases de génération. Il existe aussi (le parsing l'impose) des contraintes sur les noms de tests, qui sont donc des contraintes imposées par le framework plutôt que des contraintes liées au projet directement.

TUT

  • Version testée : 2007-07-06
  • Licence : BSD-like

TUT est original. Ce framework prend la parti du tout C++. Reposant sur une écriture par templates, il n'utilise pas le système de macros utilisés par les autres frameworks. Malheureusement, cela fait un peu exercice de style. Au final, il faut écrire beaucoup de choses pour décrire son test, là où le système de macros permets des déclarations simples.

Bien entendu, il est possible d'écrire ses propres macros pour enrober la verbosité de TUT, mais l'intérêt d'un framework est entre autre d'avoir à faire soi-même le moins de choses possibles.

Dans le même ordre d'idée, tester les exceptions se fait « à la main », en utilisant une clause d'« assert failed » dans le bloc qui convient d'un try/catch. Cela complexifie beaucoup l'écriture des tests et peu mener à des erreurs qu'évitent des noms de macros explicites comme « ASSERT_THROW » ou « ASSERT_NO_THROW ».

TUT est accompagné d'une documentation un peu faiblard et d'un site qui, lui aussi, tente l'exercice de style (une « frame » gérée avec du javascript. Cela à l'avantage de permettre la sauvegarde de tout le site en une fois, mais n'est pas aisé à la navigation).

On l'aura compris, je ne suis pas du tout convaincu par TUT. Il a cependant le mérite d'aller dans une direction originale.

Unit--

  • Version testée : 0.7.0
  • Licence : GPL

Son nom indique la couleur : minimaliste. Sa documentation le confirme : Unit-- est un framework de test unitaire réduit à sa plus simple expression. Les fonctions de tests sont en camel case, ce qui est une originalité par rapport aux macros tout en majuscules des autres framework, qui ont l'avantage de bien montrer ce qui est testé. Les fixtures se font par l'instantiation d'un objet local et les exceptions sont, comme avec TUT, testées à la main.

Pas de fioritures. Du coup, je lui trouve le même défaut que TUT : il faut, pour l'utiliser, ajouter son propre code pour être bien utilisable. Cela peut constituer une bonne base. Il a tout de même l'avantage sur TUT de n'être pas verbeux.

UnitTest++

  • Version utilisée : 1.2
  • Licence : MIT

UnitTest++ a une syntaxe claire et quelques types de tests originaux, comme les CHECK_ARRAY_*. Il est plutôt souple (sans être le summum de la souplesse, comme peut l'être CppUnit) et sa configuration de base est utilisable immédiatement.

La documentation sur sourceforge n'est pas très fournie mais semble donner les principales directions nécessaires.

Conclusion

Comme dit en introduction, tous ces frameworks se ressemblent, à quelques exceptions près, et faire un choix n'est pas évident. Ce petit parcours m'en a fait éliminer quelques-uns, ne reste plus qu'à choisir parmi ceux qui restent.