Date de première publication : 2017/09/11
La bibliothèque de tests Catch2 est une bibliothèque de tests unitaires pour le C++.
A ce jour, trois versions "co-existent" : une version 1 pour supporter le vieux CPP d'avant 2011, la version 2 dans un seul fichier d'entête pour les codes post 2011, et une v3 qui n'est plus avec un seul fichier d'entête (mais qui serait plus rapide et plus sûre)
Nous allons nous contenter de la version 2 car son utilisation est hyper simple : il suffit d'inclure le fichier d'entête catch.hpp
et de définir une petite macro pour créer un main()
capable de lancer tous les tests.
Pour avoir la dernière version disponible de Catch2, il suffit de faire :
wget https://raw.githubusercontent.com/catchorg/Catch2/v2.x/single_include/catch2/catch.hpp
Généralités
On va écrire du code de test, c'est-à-dire une ou plusieurs fonctions qui vérifient que du code que l'on a écrit a un comportement défini au préalable.
Les fonctions de tests peuvent être des succès, des échecs (bloquants ou non) et lever des exceptions.
On va utiliser principalement les macros CHECK et REQUIRE
- REQUIRE en cas d'échec arrête la fonction
- CHECK continue même en cas d'échec, mais un affichage sera fait lors de l'exécution des tests
Il existe également les formes CHECK_FALSE et REQUIRE_FALSE
Si vous avez besoin de comparer des nombres rééls, vous avez besoin d'un test avec une certaine tolérance. C'est obtenu avec Approx
REQUIRE( valeur_a_verifier == Approx( 10.2 ) );
Par défaut, si une exception est levée lors d'une condition, la condition sera supposée fausse. Il est toutefois possible d'affiner le comportement des tests vis à vis des exceptions avec les macros :
- CHECK_NOTHROW et REQUIRE_NOTHROW où aucune exception ne doit être levée (comportement par défaut)
- CHECK_THROWS et REQUIRE_THROWS où une exception, quelle quelle soit doit être levée pour que le test réussisse
- CHECK_THROWS_AS et REQUIRE_THROWS_AS où l'exception dont le type est spécifiée doit se produire
REQUIRE_THROWS_AS( m.create(), std::bad_alloc );
Code principal pour le lancements des tests
Pour exécuter les tests, il suffit de définir CATCH_CONFIG_MAIN dans un fichier de code cpp
#define CATCH_CONFIG_MAIN
#include "catch.hpp"
Exemples
Il existe plusieurs manières de rédiger des tests avec la bibliothèque. Je vais me contenter de vous en montrer une avec les macros TEST_CASE et SECTION
Pour faire simple, TEST_CASE sera l'environnement de test et cet environnement sera réinitialisé pour chaque SECTION. Vous pouvez mettre autant de SECTIONs que vous le voulez dans un TEST_CASE et écrire autant de TEST_CASEs que vous le voulez
Chaque TEST_CASE devra avoir un nom unique et pourra être paramétré par un système de tags
Voici maintenant un exemple de fichier de tests :
TEST_CASE ("Vecteur1") {
const Vecteur v;
REQUIRE ( v.capacity() >= 10 );
REQUIRE ( v.size() == 0 );
}
TEST_CASE ("Vecteur2" ) {
Vecteur v(20);
REQUIRE ( v.capacity() == 20 );
REQUIRE ( v.size() == 0 );
}
Dans l'exemple suivant, "Vecteur3" sera une batterie de tests (SECTION) qui s'appuient tous sur le même environnement de départ (fixture)
TEST_CASE ("Vecteur3" ) {
Vecteur v(5);
SECTION("ajout de quelques elements") {
REQUIRE ( v.capacity() == 5 );
for (int i=0; i<4; ++i)
v.push_back(i*1.0);
REQUIRE ( v.size() == 4 );
}
SECTION("tableau un peu agrandi") {
// on peut verifier que vecteur est bien un nouveau :-)
REQUIRE ( v.capacity() == 5 );
for (int i=0; i<6; ++i)
v.push_back(i*1.0);
REQUIRE ( v.capacity() == 10 );
REQUIRE ( v.size() == 6 );
}
SECTION("on verifie les valeurs dans le vecteur") {
for (int i=0; i<25; ++i)
v.push_back(i*1.0);
REQUIRE( v.capacity() == 40 );
REQUIRE( v.size() == 25 );
for (int i=0; i<25; ++i)
CHECK(v[i] == Approx(i*1.0+0.1)); // :-)
}
SECTION("on verifie les exceptions") {
REQUIRE_THROWS_AS( v[-1] == 0, Vecteur::OutOfRangeException);
REQUIRE_THROWS_AS( v [6] == 0, std::bad_alloc); // :-)
}
}
Bien entendu, les tests commentés avec des smileys ne sont pas validés
Voici une copie écran possible de ce que l'on peut obtenir :
Pour finir, je voudrais juste ajouter que l'on peut choisir les tests à exécuter avec les tags et choisir le format de sortie, entre autres ...