tete du loic

 Loïc YON [KIUX]

  • Enseignant-chercheur
  • Référent Formation Continue
  • Responsable des contrats pros ingénieur
  • Référent entrepreneuriat
  • Responsable de la filière F2 ingénieur
  • Secouriste Sauveteur du Travail
mail
loic.yon@isima.fr
phone
(+33 / 0) 4 73 40 50 42
location_on
ISIMA
  • twitter
  • linkedin
  • viadeo

[++C] Google Test

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 :

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

Ces macros se diversifient en fonction des types à comparer : entier, réel, booléen, chaînes de caractères C

Ressources

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 ..