Date de première publication : 2015/10/15
Date de dernière modification : 2017/07/23
Au 1er septembre 2017, la bibliothèque Google Test n'est plus utilisée pour le cours ISIMA ZZ2 de C++. Elle est remplacée par la bibliothèque Catch
GoogleTest est un framework de tests "unitaires" (Google ne les appelle pas comme cela) inspiré de jUnit pour ceux qui connaissent.
On peut l'utiliser de deux manières :
- Une manière rapide mais non recommandée, avec une bibliothèque à lier à un exécutable.
- Une manière recommandée avec une copie locale par projet et l'utilisation de cmake [ZZ3]
Concepts et utilisation
Généralités
On va écrire du code de test, c'est-à-dire une ou plusieurs fonctions qui prennent en paramètres : une classe qui représente une fixture (environnement de tests) et un nom de test
Les fonctions de tests peuvent être des succès, des échecs (bloquants ou non) et lever des exceptions.
On va utiliser principalement les macros EXPECT et ASSERT
- ASSERT en cas d'échec arrête la fonction
- EXPECT continue même en cas d'échec, mais un affichage sera fait lors de l'exécution des tests
Ces macros se diversifient en fonction des types à comparer : entier, réel, booléen, chaînes de caractères C
Ressources
- Google Test est téléchargeable sur GitHub
- La documentation est disponible sur Google Code [débutant] [avancé] [FAQ]
Code principal pour le lancements des tests
Voici le code du programme principal qui lance les tests écrits dans le fichier vecteur_test.hpp
. Si on ne veut pas l'écrire,
il suffit d'inclure la bibliothèque libgtest_main
.
#include <gtest/gtest.h>
#include "vecteur_test.hpp"
int main(int argc,char *argv[]) {
::testing::InitGoogleTest(&argc,argv);
return RUN_ALL_TESTS();
}
Exemples
Voici maintenant un exemple de fichier de tests :
#ifndef _VECTEUR_TEST_HPP_
#define _VECTEUR_TEST_HPP_
#include "vecteur.hpp"
#include <iostream>
TEST ( TestVecteur, Vecteur1 ) {
const Vecteur v;
EXPECT_EQ( v.capacity(), 10 );
EXPECT_EQ( v.size() , 0 );
}
TEST ( TestVecteur, Vecteur2 ) {
Vecteur v(20);
EXPECT_EQ( v.capacity(), 20 );
EXPECT_EQ( v.size() , 0 );
}
Dans le troisième test intitulé Vecteur3, si la capacité du vecteur n'est pas de 5, l'exécution du test s'arrête.
TEST ( TestVecteur, Vecteur3 ) {
Vecteur v(5);
ASSERT_EQ( v.capacity(), 5 );
for (int i=0; i<4; ++i)
v.push_back(i*1.0);
EXPECT_EQ( v.size() , 4 );
}
TEST ( TestVecteur, Vecteur4 ) {
Vecteur v(5);
for (int i=0; i<6; ++i)
v.push_back(i*1.0);
EXPECT_EQ( v.capacity(), 10 );
EXPECT_EQ( v.size() , 6 );
}
Dans le cinquième test, on compare les éléments du tableau (des réels) avec ce que l'on voulait mettre dedans.
TEST ( TestVecteur, Vecteur5 ) {
Vecteur v(5);
for (int i=0; i<25; ++i)
v.push_back(i*1.0);
EXPECT_EQ( v.capacity(), 40 );
EXPECT_EQ( v.size() , 25 );
for (int i=0; i<25; ++i)
EXPECT_DOUBLE_EQ(v[i], (i*1.0));
}
Dans le sixième test, l'accès à un élément en dehors des limites du vecteur doit provoquer une exception, sinon le test est un échec.
TEST ( TestVecteur, Vecteur6 ) {
Vecteur v(5);
EXPECT_THROW(std::cout<< v[-1], Vecteur::OutOfRangeException);
EXPECT_THROW(std::cout<< v [6], Vecteur::OutOfRangeException);
}
#endif
Tous les tests utilisent la même classe de test (fixture) TestVecteur qui est définie implicitement car on n'a pas besoin de données communes aux tests.
Voici une copie écran possible de ce que l'on peut obtenir :
Solution rapide : édition des liens statiques
Cette méthode est intéressante car elle est extrêment rapide à mettre en place mais elle peut provoquer des erreurs de diagnostic si les options de compilation ne sont pas bonnes :
Sur etud,
g++ main_test.cpp vecteur.cpp /opt/gtest/lib/libgtest.a -pthread -I/opt/gtest/include
libgtest.a
est un fichier de bibliothèque, il est lié à l'exécutable (comme un fichier .o). -pthread
est une option nécessaire à la compilation ET à l'édition des liens, -I/opt/../include
est une option de compilation. Avec ces informations vous pouvez modifier votre makefile. Si vous vous posez la question de pourquoi on lie directement avec le .a plutôt qu'utiliser l'option -lgtest
, c'est ici :
Un fichier d'extension .a est une bibliothèque statique. Etud est configurée pour charger et utiliser des bibliothèques dynamiques. Il existe des options pour g++ pour gérer le type de bibliothèques mais je n'ai pas réussi à utiliser les deux types en même temps sauf de cette manière-là.
Sur MacOS, avec l'installation par défaut (MacPorts) :
$ g++ main_test.cpp vecteur.cpp -pthread -lgtest -I/opt/local/include -L/opt/local/lib
main_test.cpp
est le fichier qui lance les tests donné au paragraphe précédent. vecteur.cpp
est l'implémentation du code à tester
(vecteur.hpp
est le fichier d'entête) conformément à l'énoncé du TP 5 de C++ ZZ2.
L'installation par défaut se fait dans /opt/local
, donc la bibliothèque statique libgtest.a
se trouve dans /opt/local/lib
et les fichiers d'entête de Google Test se trouvent dans /opt/local/include
(et non pas dans /usr/include
, là où se trouvent les fichiers standards
du C/C++).
La bibliothèque Google Test utilise les threads, il faut donc ajouter la bibliothèque pthread (option à mettre à l'édition des liens).
La bibliothèque ne doit pas être compilée avec les options -ansi -pedantic
ZZ3 : utilisation avec cmake
La procédure est décrite dans le premier TP de C++ ZZ3 disponible sur l'ENT. Je la reprendrai et la complèterai plus tard
La bibliothèque Google Test utilise les threads, il faut donc ajouter la bibliothèque pthread, cela se fait dans add_target_librairies. La bibliothèque ne doit pas être compilée avec les options -ansi -pedantic
A chaque compilation, il y a vériifcation de la version de Google Test, pour désactiver cela, il suffit de mettre la ligne commençant par SVN en commentaire (#) dans gtest.cmake
Précisions avec Mac Port
À chaque compilation, le script vérifie si la version de Google Test utilisée est bien la dernière. J'ai eu un problème de certificat au niveau de SVN. La commande suivante m'a permis de les accepter
svn list https://github.com/stp/googletest
Je n'ai pas encore réussi à faire marcher Google Test avec clang++
mais j'ai réussi avec g++
. Il faut préciser quel compilateur utiliser de la manière suivante :
CC=gcc CXX=g++ cmake ..