TP Cppunit
TP Cppunit
Sommaire
Tests Logiciels 2
Notion de test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
Tests unitaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
Tests d’intégration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
Tests de validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
Tests de recette . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Autres tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
CppUnit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Travail demandé 6
Objectifs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
La classe ISBN à tester . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
Séquence 1 : la classe de test TestUnitaireISBN . . . . . . . . . . . . . . . . . . . . . . . . . . 11
Séquence 2 : développement piloté par les tests (TDD) . . . . . . . . . . . . . . . . . . . . . . 15
Séquence 3 : tests de non régression avec refactorisation du code . . . . . . . . . . . . . . . . . 16
Séquence 4 : une GUI pour les tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
Séquence 5 : une application sous Qt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
1
TESTS LOGICIELS
Tests Logiciels
Notion de test
Le test est une recherche d’anomalie (ou défaut, appelé souvent bug en informatique) dans le
comportement d’un logiciel.
Si une batterie de tests ne montre pas de défaut cela n'implique pas que le logiciel est quand même
exempt de défaut ...
Le test n'a pas pour objectif : de diagnostiquer la cause des erreurs, de corriger les fautes ou de prouver
la correction !
A l’origine, il y a la faute (mistake) : c’est la cause d’une erreur (error). Un défaut (bug) est la
manifestation d’une erreur dans un logiciel. Un défaut peut causer une panne (failure). Une panne
est la fin de la capacité d’un système ou de l’un de ses composants d’effectuer la fonction requise, ou de
l’effectuer à l’intérieur des limites spécifiés.
On évalue à environ 40% la part des tests dans le coût d'un logiciel (et plus pour des logiciels critiques).
Les tests logiciels en BTS couvrent : les tests unitaires, les test d’intégration et les tests de validation
(recette).
La recette est la phase de validation de la conformité en rapport au cahier des charges fonctionnel.
Tests unitaires
Chaque module du logiciel est testé séparément par rapport à ses spécifications. En programmation
C++, on fera des tests unitaires au niveau des méthodes (fonctions), puis au niveau de la
classe.
Les méthodes Extreme programming (XP) ou Test Driven Development (TDD) ont remis les tests uni-
taires (TU ou UT), appelés tests du programmeur , au centre de l'activité de programmation.
Tests d’intégration
Les modules validés par les tests unitaires sont rassemblés dans un composant logiciel. Le test d’intégration
vérifie que l’intégration des modules n’a pas altéré leur comportement.
Tests de validation
Le test vérifie que le logiciel réalisé correspond bien aux besoins exprimés par le client. La validation ou
vérification d’un produit cherche donc à s’assurer qu’on a construit le bon produit.
Tests de recette
L’application doit fonctionner dans son environnement de production (tests d’intégration système),
avec les autres applications présentes sur la plate-forme et avec le système d’exploitation. Les clients
utilisateurs vérifient sur site que le système répond de manière parfaitement correcte.
Autres tests
En informatique, les tests logiciels sont nombreux : tests de boîte noire (ou test fonctionnel), tests de
boîte blanche (test structurel), tests de conformité ou de non conformité, tests fonctionnels, tests de non
régression (ou de régression), tests de robustesse (d’endurance, de fiabilité), tests de charge et de montée
en charge, tests aux limites, tests de stress, ...
CppUnit
À l’origine, Kent Beck crée l’environnement de test sUnit pour le langage Smalltalk en octobre 1994. En
1997, Kent Beck rencontre Erich Gamma avec lequel il crée JUnit qui, suite à sa popularité, entraînera
la création de nombreux frameworks de tests unitaires, cet ensemble se nomme xUnit.
Le terme générique « xUnit » désigne un outil permettant de réaliser des tests unitaires dans un langage
donné (dont l’initiale remplace « x » le plus souvent) : CppUnit pour le C++, CUnit pour le C, PHPUnit
pour PHP, JUnit pour Java, etc ...
La plupart des frameworks de la famille xUnit permettent la génération des classes de test unitaire.
Cependant ces frameworks ne fournissent que le squelette des classes. Les tests devront donc être écrits
par le développeur.
CppUnit est donc l’équivalent C++ de l’outil JUnit créé entre autres par Kent Beck.
Liens :
– http ://sourceforge.net/projects/cppunit/
– http ://cppunit.sourceforge.net/doc/cvs/cppunit_cookbook.html
– http ://matthieu-brucher.developpez.com/tutoriels/cpp/cppUnit/
Installation de CppUnit sur Ubuntu :
$ sudo apt-get install libcppunit-dev
$ cppunit-config --version
1.12.1
$ cppunit-config --cflags
$ cppunit-config --libs
-lcppunit -ldl
Principe
Le principe d’un test unitaire (T.U.) est simple : on va tester chaque fonction ou méthode individuellement
(unitairement) et séparément par rapport à ses spécifications (c’est-à-dire ce qu’elle doit faire ou plus
précisément ce que l’on attend d’elle).
Pour réaliser un test unitaire d’une fonction ou méthode, on procède de la manière suivante :
• on choisit un jeu de tests : c’est-à-dire les données d’entrée de la fonction (ses arguments)
• on définit le résultat attendu : c’est-à-dire le résultat qu’elle doit fournir
• on vérifie le résultat obtenu avec le résultat attendu
On validera une fonction lorsque tous les jeux de tests choisis ne détectent aucune anomalie (un résultat
obtenu différent du résultat attendu). Toute la difficulté est de bien choisir ses jeux de tests pour couvrir
le maximum de cas afin de détecter un bug.
Préparation
Un test doit être non intrusif. Il faut tout d’abord penser à séparer le code source de l’application de la
suite de tests.
Exemple :
• src : répertoire contenant l’ensemble du code source de l’application
• tests : répertoire contenant l’ensemble du code source des tests unitaires de l’application
Le code source doit être intègre, c'est-à-dire non modié pour réaliser le test. Par exemple pour un test
fonctionnel on ne doit pas ajouter de saisie, de printf ou de TRACE qui modie entre autre les temps
d'exécution et donc la validité du module testé.
classe d’équivalence déterminée entraîne un résultat soit correct (classe valide), soit incorrect (classe
invalide).
Une fois les classes déterminées, il suffit de prendre au moins un représentant pour chacune de ces classes
(ce sera un jeu de test).
Un plan de test se représente par un tableau contenant : une description du test, les valeurs en entrées
de la fonction et le résultat attendu.
Un rapport de test correspondra au tableau ci-dessus complété avec la possibilité d’ajouter des remarques
et hypothèses en cas d’échec.
Action corrective
À partir du rapport de test, le développeur en charge de cette fonction propose une action corrective sur
le code source. Cette activité ne fait pas partie de la procédure de tests.
Travail demandé
Objectifs
Les objectifs de ce tp sont de mettre en oeuvre les procédures de tests unitaires en utilisant le framework
CppUnit.
Avant 2007, le numéro ISBN-10 se composait de quatre segments, trois segments de longueur variable et
un segment de longueur fixe, la longueur totale de l’ISBN comprenait 10 chiffres. Depuis le 1er janvier
2007, la longueur a été étendue à 13 chiffres en ajoutant un groupe initial de 3 chiffres.
Pour faciliter leur gestion informatique, chaque livre porte un code à barres à la norme EAN 13 et un
code ISBN-13 dont il est dérivé.
Ce code comporte 13 chiffres et est aujourd’hui obligatoire (depuis janvier 2007), et doit être utilisé
pour tous les nouveaux codes ISBN à la place du code à 10 chiffres. En effet, l’ancienne numérotation est
arrivée à saturation et ne permettrait plus d’attribuer simplement des groupes de codes spécifiques aux
différents éditeurs.
La conversion d’un ancien code ISBN-10 en code ISBN-13 compatible avec la norme EAN est automatique
(et obligatoire pour toutes les transactions électroniques à compter de janvier 2007).
La classe ISBN possèdera donc un attribut isbn de type string pour conserver le code. Le type string
est le meilleur choix car :
– les codes ISBN sont structurés par segment et il sera donc plus simple de traiter une chaîne de caractère
qu’un nombre
– les codes ISBN séparent les différents segments par un tiret : "2-266-11156-6" correspond au code
2266111566
La classe ISBN permettra de gérer des codes ISBN-10 et des codes ISBN-13.
Les codes ISBN comportent une clé de contrôle calculée à partir des 9 chiffres précédents pour un code
ISBN-10 et des 12 chiffres précédents pour un code ISBN-13.
La classe ISBN offrira les services suivants :
– une méthode nettoie() qui reçoit un code isbn de type string et qui retourne le code isbn sans les
tirets (pour un code ISBN-10, "2-266-11156-6" → − "2266111566")
– les méthodes getTaille() qui retournent le nombre de chiffres contenus dans un code isbn “nettoyé”
de type string (pour un code ISBN-10, "2-266-11156-6" → − 10)
– les méthodes estValide() qui retournent vrai (true) pour un code isbn valide (en taille, en chiffres
et en clé de contrôle) sinon elles retournent faux (false) : pour un code ISBN-10, "2-266-11156-6"
→
− true
Si le code ISBN n'est pas valide (en taille, en chires ou en clé de contrôle), il ne doit pas être stocké
dans l'attribut isbn. Dans ce cas, une chaîne vide sera aectée à l'attribut. Ce comportement devra être
implémenté pour le constructeur et le setter setISBN(). Si il est valide, il sera conservé dans sa forme initiale
non nettoyée ("2-266-11156-6" par exemple).
#include <iostream>
class ISBN
{
private:
std::string isbn;
public:
ISBN();
ISBN(const std::string &isbn);
// Setter
void setISBN(const std::string &isbn);
std::string getISBN() const;
// Services
int getTaille() const;
int getTaille(const std::string &isbn) const;
bool estValide() const;
bool estValide(const std::string &isbn) const;
std::string nettoie(const std::string &isbn) const;
};
#endif
Code 2 – isbn.h
Remarques :
– 11 étant un nombre premier, une erreur portant sur un chiffre entraînera automatiquement une
incohérence du code de contrôle.
– La vérification du code de contrôle peut se faire en effectuant le même calcul sur le code ISBN complet,
en appliquant la pondération 10 au dixième chiffre de la clé de contrôle (si ce chiffre clé est X, on lui
attribue la valeur 10) : la somme pondérée doit alors être un multiple de 11.
Lire : http ://fr.wikipedia.org/wiki/International_Standard_Book_Number
#include "isbn.h"
/**
Vérifie un code ISBN-10
*/
bool ISBN::estValide10(const std::string &isbn) const
{
int ponderation = 10;
int somme = 0;
int codeDeControle = 0;
/* seulement ISBN 10 */
if(_isbn.size() != ISBN_10)
{
std::cerr << "Le␣code␣ISBN␣n’a␣pas␣la␣bonne␣taille␣(ISBN␣10␣accepté)" << std::endl;
return false;
}
return false;
}
/**
Nettoie un code ISBN en enlevant les tirets
*/
std::string ISBN::nettoie(const std::string &isbn) const
{
std::string _isbn = "";
return _isbn;
}
...
#include <cppunit/extensions/HelperMacros.h>
private:
ISBN *isbn; // un pointeur sur une instance de la classe à tester
public:
TestUnitaireISBN();
virtual ~TestUnitaireISBN();
#endif
Code 4 – TestUnitaireISBN.h
#include "TestUnitaireISBN.h"
#include "isbn.h" // Classe à tester
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
TestUnitaireISBN::TestUnitaireISBN()
{
TestUnitaireISBN::~TestUnitaireISBN()
{
void TestUnitaireISBN::setUp()
{
// Initialisation pour les tests
isbn = new ISBN; // instancie un objet ISBN à tester
}
void TestUnitaireISBN::tearDown()
{
delete isbn; // libère la mémoire allouée à l’objet ISBN
}
void TestUnitaireISBN::testEstValideISBN10()
{
// Initialisation du test
// Classes invalides :
CPPUNIT_ASSERT_EQUAL( false, isbn->estValide("2-266-11156-5") ); // mauvaise code de
controle
CPPUNIT_ASSERT_EQUAL( false, isbn->estValide("206-11156-6") ); // mauvaise longueur
CPPUNIT_ASSERT_EQUAL( false, isbn->estValide("2-XXX-11156-6") ); // mauvais codes
CPPUNIT_ASSERT_EQUAL( false, isbn->estValide("") ); // code vide
// Technique détaillée :
bool resultatObtenu;
bool resultatAttendu = true;
void TestUnitaireISBN::testEstValideISBN13()
{
// Initialisation du test
// Classes invalides :
CPPUNIT_ASSERT_EQUAL( false, isbn->estValide("978-2-86889-006-5") ); // mauvaise code de
controle
CPPUNIT_ASSERT_EQUAL( false, isbn->estValide("78-2-86889-006-1") ); // mauvaise longueur
CPPUNIT_ASSERT_EQUAL( false, isbn->estValide("978-2-86889-AAA-1") ); // mauvais codes
CPPUNIT_ASSERT_EQUAL( false, isbn->estValide("908-2-86889-006-1") ); // mauvais
identifiants attribués aux livres dans la codification EAN
CPPUNIT_ASSERT_EQUAL( false, isbn->estValide("") ); // code vide
}
void TestUnitaireISBN::testSetterISBN10()
{
// Classe valide :
isbn->setISBN("2-266-11156-6");
CPPUNIT_ASSERT( isbn->getISBN() == "2-266-11156-6" );
// etc ...
// Classes invalides :
isbn->setISBN("2-266-11156-5"); // mauvaise code de controle
void TestUnitaireISBN::testSetterISBN13()
{
// TODO
CPPUNIT_FAIL( "TODO" ); // on fait échouer le test volontairement en attendant de l’
écrire
}
void TestUnitaireISBN::testNettoieISBN()
{
std::string codeISBN;
// Classe valide :
codeISBN = isbn->nettoie("2-266-11156-6");
CPPUNIT_ASSERT( codeISBN == "2266111566" );
codeISBN = isbn->nettoie("978-2-86889-006-1");
CPPUNIT_ASSERT( codeISBN == "9782868890061" );
}
Les test s'appuient sur des assertions fournies par CppUnit. La liste des assertions disponibles dans le
framework CppUnit est fournie en Annexe 3.
Le framework CppUnit fournit un programme de test générique (le code source est fourni en Annexe 2).
On fabrique et on lance le programme de tests :
$ make
$ ./tests/testISBN
TestUnitaireISBN::testEstValideISBN10 : assertion
TestUnitaireISBN::testEstValideISBN13 : assertion
TestUnitaireISBN::testSetterISBN10 : assertion
TestUnitaireISBN::testSetterISBN13 : assertion
TestUnitaireISBN::testNettoieISBN : OK
TestUnitaireISBN.cpp:40:Assertion
Test name: TestUnitaireISBN::testEstValideISBN10
equality assertion failed
- Expected: 1
- Actual : 0
TestUnitaireISBN.cpp:70:Assertion
Test name: TestUnitaireISBN::testEstValideISBN13
equality assertion failed
- Expected: 1
- Actual : 0
TestUnitaireISBN.cpp:84:Assertion
Test name: TestUnitaireISBN::testSetterISBN10
assertion failed
- Expression: isbn->getISBN() == "2-266-11156-6"
TestUnitaireISBN.cpp:100:Assertion
Test name: TestUnitaireISBN::testSetterISBN13
forced failure
- TODO
Failures !!!
Run: 5 Failure total: 4 Failures: 4 Errors: 0
Bilan : 1 test sur 5 est passé ! Seule la méthode nettoie() a été validée par son test unitaire. Le test
unitaire de validation d’un code ISBN-13 a échoué normalement car la méthode estValideISBN13() n’a
pas encore été implémentée (cf. séquence 2).
Par contre, la méthode ISBN::estValideISBN10() comporte un défaut. Ce défaut provoque aussi une
panne dans les méthodes get/set de l’attribut isbn.
Question 2. Le jeu de test pour valider la méthode ISBN::estValideISBN10() est incomplet car il
manque une classe valide à tester. À partir du code source ISBN::estValideISBN10(), compléter la
méthode testEstValideISBN10() pour qu’elle couvre tous les classes valides.
Une code ISBN-13 comporte 13 chiffres et est aujourd’hui obligatoire (depuis janvier 2007), et doit
être utilisé pour tous les nouveaux codes ISBN à la place du code à 10 chiffres. En effet, l’ancienne
numérotation est arrivée à saturation et ne permettrait plus d’attribuer simplement des groupes de codes
spécifiques aux différents éditeurs.
La conversion d’un ancien code ISBN-10 en code ISBN-13 compatible avec la norme EAN est automatique
(et obligatoire pour toutes les transactions électroniques à compter de janvier 2007). Ce code comporte
13 chiffres, et est calculé à partir du numéro ISBN-10 de la façon suivante :
– les trois premiers chiffres valent « 978 » (978 est le premier des identifiants attribués aux livres dans la
codification EAN) ou « 979 » ;
– les neuf chiffres suivants sont les neuf premiers chiffres de l’ISBN-10 (code de la zone géographique,
code de l’éditeur, numérotation interne à l’éditeur) ;
– le dernier chiffre (c13) est une clé de contrôle calculée en fonction des 12 premiers chiffres en calculant
le reste de la division par 10 (le modulo 10) de la différence entre 10 et le modulo 10 de la somme
de chaque chiffre, chaque chiffre étant pondéré selon un indice de position égal à 1 pour les positions
impaires et 3 pour les positions paires, soit suivant la formule :
c13 = modulo(10 - modulo(c1 + 3 × c2 + c3 + 3 × c4 + ... + c11 + 3 × c12, 10), 10),
– le chiffre clé obtenu ne peut varier que de 0 à 9 (il n’y a plus de chiffre X),
– la vérification de la clé de contrôle peut se faire en vérifiant que la somme pondérée (calculée sur les
13 chiffres) est bien un multiple de 10.
À la n de cette séquence, l'ensemble des méthodes de la classe ISBN doivent être validées et passer
les tests unitaires.
La refactorisation de code ( refactoring ) consiste à retravailler le code source sans ajouter de fonctionnalités
au logiciel ni corriger de bogues, mais en améliorant sa lisibilité pour simplier sa maintenance, ou le rendre
plus générique. On parle aussi de remaniement.
Question 6. Ajouter un test unitaire pour la méthode getTaille() et lancer le programme de test
sous Qt.
On va créer un nouveau widget qui intégre la saisie et la validation automatique d’un code ISBN :
#ifndef MYWIDGET_H
#define MYWIDGET_H
#include <QtGui>
class ISBN;
public:
MyWidget( QWidget *parent = 0 );
virtual ~MyWidget();
public slots:
void valider( const QString & code );
};
#endif
Code 8 – mywidget.h
// Initialisation
isbn = new ISBN();
if(isbn->estValide())
setVert();
else
setRouge();
MyWidget::~MyWidget()
{
delete isbn;
}
void MyWidget::setRouge()
{
QImage *image;
QPixmap pixmap;
pixmap = QPixmap::fromImage(*image);
etatISBN->setPixmap(pixmap);
delete image;
}
void MyWidget::setVert()
{
QImage *image;
QPixmap pixmap;
{
qWarning("Fichier␣introuvable␣!");
}
pixmap = QPixmap::fromImage(*image);
etatISBN->setPixmap(pixmap);
delete image;
}
Code 9 – mywidget.cpp
Question 7. Compléter la définition de la classe MyWidget (le slot valider()) puis fabriquer un
exécutable.
/* Lire : http://fr.wikipedia.org/wiki/International_Standard_Book_Number */
/**
Vérifie un code ISBN-10
*/
bool ISBN::estValide10(const std::string &isbn) const
{
int ponderation = 10;
int somme = 0;
int codeDeControle = 0;
/* seulement ISBN 10 */
if(_isbn.size() != ISBN_10)
{
std::cerr << "Le␣code␣ISBN␣n’a␣pas␣la␣bonne␣taille␣(ISBN␣10␣accepté)" << std::endl;
return false;
}
return false;
}
/**
Vérifie un code ISBN-13
*/
return false;
}
/**
Nettoie un code ISBN en enlevant les tirets
*/
std::string ISBN::nettoie(const std::string &isbn) const
{
std::string _isbn = "";
return _isbn;
}
ISBN::ISBN()
{
this->isbn = "";
}
this->isbn = isbn;
return;
}
this->isbn = "";
}
Asserts that two values are equals, provides additional message on failure.
CPPUNIT_ASSERT_EQUAL_MESSAGE(message,expected,actual)
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(message,expected,actual,delta)
Fails with the specified message (Always fails and abort current test with the given message
).
message Message reported in diagnostic.
CPPUNIT_FAIL( message )
QxCppUnit::TestRunner runner;
runner.addTest(CPPUNIT_NS::TestFactoryRegistry::getRegistry().makeTest());
runner.run();
return 0;
}
Code 13 – main.cpp