Gestion des tests dans Qt Creator

La dernière version de Qt Creator 4.3.0 bénéficie d’une nouvelle fonctionnalité remarquable : la gestion des Tests ! Auparavant, pour faire cela, il fallait fabriquer soi-même un gestionnaire ou utiliser un outil tiers. Maintenant, exécuter les tests unitaires depuis Qt Creator devient un jeu d’enfant, alors plus aucune excuse pour faire des tests unitaires !

La nouvelle interface permet de gérer les tests Qt Tests (C++), Quick Test (QML) et Google Tests. On peut choisir de les exécuter tous en même temps, pratique pour valider un projet, ou bien individuellement pour valider une nouvelle classe ou méthode. Sélectionnez la vue “Test” dans le panneau de droite et dans la panneau de sortie, cliquez sur le bouton “Test results” pour voir les résultats.



L’application montrée ci dessous dispose de tests unitaires Qt Tests. Aller dans le menu “Outils” / “Tests” / “Rescan Tests” si ces derniers n’apparaissent pas.

L’organisation du projet est découpée en trois parties : une application, une librairie et des tests unitaires. Ce découpage permet de concentrer les test unitaires sur la librairie, et les tests fonctionnels sur l’application. Pour plus de détails sur l’organisation du projet, vous pouvez référer à l’article “Regrouper application, librairies et tests dans un seul projet Qt”.

Qt Tests


Trois types de tests sont disponibles dans l’interface :  
  1. Qt Test : tests unitaires en C++
  2. Quick tests : les tests unitaires en QML
  3. Google Test : tests nécessitant l’installation du framework Google.

Dans cet article, il sera question uniquement des tests en C++ Qt Test. Pour aller plus loin sur les autres types de tests, commencez par regarder cet article : Running Autotests.

Classes de tests

Chaque groupement de tests (TestListPerson, TestPerson dans l'exemple ci-dessous) correspond à une classe en C++. Chacune de ces classes est définie dans un projet indépendant (tst_ListPerson, tst_Person) qui dispose de sa propre fonction main().

Ce n’est pas une obligation, mais bien souvent on va avoir une correspondance entre une classe et un projet de tests, par exemple :
  • classe Person correspond au projet de tests unitaires tst_Person
  • classe ListPerson correspond au projet de tests unitaires tst_ListPerson

Méthode de tests

Sous chaque classe de test, apparaît la liste des tests à proprement dit. Cette liste correspond à chaque méthode définie dans la classe en tant que “private slots”.

class TestListPerson : public QObject
{
    Q_OBJECT


public:
    TestListPerson();
    ~TestListPerson();


private slots:
    void initTestCase();
    void cleanupTestCase();
    
    void init();
    void cleanup();
    
    void testAdd();
    void testSearch();
    void testAverageAge();
};

Les méthodes initTestCase() et cleanupTestCase() sont appelées avant et après l’exécution des tests de la classe. Elles peuvent contenir des vérifications, comme par exemple tester le constructeur et le destructeur de la classe. C’est pour cela qu’en plus des méthodes testAdd, testSearch et testAverageAge, on les voit apparaître dans le panneau de résultats.



A contrario, les méthodes init() et cleanup() ne sont pas visibles en tant que résultat de test. Elles sont appelées avant et après l’exécution de chacune des méthodes de tests (testAdd, testSearch et testAverageAge). Elles n’ont pas pour vocation de vérifier une condition mais plutôt d’initialiser et nettoyer un contexte. Pour allez plus loin dans le concept de tests unitaires, lire le tutoriel de la documentation de Qt : Qtest Tuorial.

Fonction main()

Généralement, pour simplifier l’écriture d’un projet de tests, on fait appel à la macro QTEST_MAIN(Object) pour générer la méthode main qui instanciera les bons objets et exécutera les tests. A la fin du fichier décrivant la classe TestListPerson, on aura quelque chose comme ceci :

QTEST_APPLESS_MAIN(TestListPerson) 
#include "tst_testlistperson.moc"

A la place de QTEST_MAIN, on utilise QTEST_APPLESS_MAIN() car il n’y a pas de GUI, ni d’objet de type QObject disposant de signaux et de slots à tester. Donc inutile d’instancier un objet de type QApplication, ni de lancer la boucle d’événements. L’inclusion du fichier .moc est nécessaire dans le cas où la déclaration et l’implémentation de la classe sont dans le même fichier.

Fichier de définition du projet

Au niveau de la configuration de chaque projet de tests, il faut définir dans le fichier .pro l’option de config “testcase” (CONFIG += testcase). Voici un exemple simple de test :
fichier tst_ListPerson.pro
include (../../../app.pri)

QT += testlib
QT -= gui

CONFIG += qt console warn_on depend_includepath 
CONFIG += testcase
CONFIG -= app_bundle

TEMPLATE = app

SOURCES +=  tst_testlistperson.cpp

Exécution des tests

L’exécution des tests devient un jeu d’enfant. Un clique droit sur le label “Qt Test”, et c’est parti pour l’exécution de tous les tests, ou bien de ceux uniquement sélectionnés. Un clique droit sur un test en particulier, pour n’exécuter que ce dernier, etc.


L’exécution de tous les tests ou bien le ciblage d’un en particulier autorise ainsi une grande souplesse d’utilisation.

Lecture des résultats

Le panneau de droite “Tests results” montre les résultats des tests avec un code couleur. Soit toutes les vérifications ont réussi, et dans ce cas, il est marqué “PASS” en vert. Soit l’une des vérifications a échoué, et il est marqué comme FAIL. En cliquant sur la ligne “FAIL”, on obtient plus d’information sur l’erreur et sa nature. En double cliquant sur la ligne FAIL, Qt Creator ouvre le fichier et positionne le curseur sur le test qui a échoué.


Dans chaque méthode de test, il peut y avoir la présence d’une ou plusieurs vérifications de type QVERIFY, QVERIFY2, QCOMPARE… Le panneau de résultat ne détaille pas toutes les vérifications. Quand l’ensemble des tests sont vérifiés, le panneau affiche “PASS” pour la méthode compléte, sans détails.
Pour avoir plus de détails, il faut faire un “run” du projet de test individuellement en rajoutant l’option “-v2” dans la ligne de commande pour l’exécution.
Enfin, quand on déplie les différents résultats, on retrouve également les informations de debug (qDebug, qWarn, qInfo, qFatal), ce qui peut s’avérer fort utile.

Conclusion

Cet outil graphique de gestion des tests comble une lacune importante de l’IDE Qt Creator. Il facilite grandement la tâche des développeurs, en proposant l’exécution des tests unitaires et en affichant les résultats. La difficulté étant d’organiser correctement son projet afin de rendre tout cela possible.
Personnellement, c’est un soulagement, plus besoin de créer une fonction en C++ ou en Perl chargée de lancer les tests et d'interpréter les résultats dans la jungle d’un fichier XML… Donc chapeau aux développeurs et merci pour cette super fonctionnalité !

Regrouper applications, librairies et tests dans un seul projet Qt.

Voici un article qui présente une manière d’organiser son projet Qt de façon à avoir l’application, les librairies et les tests dans un seul et même projet. Il existe d’autres façons de faire, mais dans la jungle des possibilités (et des fichiers .pro), avoir un patron qui fonctionne, ça peut aider.
L’organisation du projet reprend les concepts décrits dans l’article sur la gestion des dépendances et la compilation répartie : SUBDIRS - handling dependencies. Le projet donné en exemple est donc découpé en une application app, une librairie lib et des tests tst_class1 et tst_class2, comme ceci :
 
 /projet
 |--- projet.pro
 |--- app.pri
 `--- /src
      |--- /app
      |    |--- app.pro
      |    `--- ... 
      |--- /lib
      |    |--- lib.pro
      |    `--- ... 
      `--- /tests
           |--- /tst_class1
           |    |--- tst_class1.pro
           |    `--- ... 
           `--- /tst_class2
                |--- tst_class2.pro
                `--- ... 

L’application et les tests dépendent de la libraire. La présence d’une ou plusieurs librairies est très importante car cela permet d’isoler les composants et de les tester efficacement. Pour plus d’informations sur ce sujet, je vous conseille de lire “QtTest Unit Testing Framework” de Justin Noel de la société ICS.

Le projet

Le projet global contient un fichier project.pro très simple qui décrit simplement les sous-répertoires (subdirs) et les dépendances.
fichier project.pro
TEMPLATE = subdirs
 
SUBDIRS = \
    lib \
    app \
    tests

# folders where to find the sub projects
lib.subdir = src/lib
app.subdir  = src/app
tests.subdir = src/tests
 
# dependencies
app.depends = lib
tests.depends = lib

Les deux dernières lignes décrivent les dépendances de l’application et des tests vis à vis de la librairie. Celle-ci sera donc compilée en premier. Ensuite l’application et les tests pourront être compilés en parallèle.

La librairie

La librairie contient l’ensemble des classes utiles à l’application. Pour créer celle-ci, on peut s’aider du wizard de création de Qt Creator.
Rien de particulier concernant le fichier .pro de la librairie. La seule chose importante, c’est la définition de la variable DESTDIR qui va copier le résultat de la compilation (la librairie, la DLL, les fichiers de symboles pdb) dans un répertoire “lib” situé juste en dessous de la racine du projet. Les autres modules pourront ainsi facilement y accéder pour linker correctement leur exécutable.
fichier lib.pro
QT -= gui

TARGET = ProjectLib
CONFIG (release, debug|release) {
    TARGET = ProjectLib
    DESTDIR = $$PWD/../../lib/debug
}
CONFIG (debug, debug|release) {
    TARGET = ProjectLibd
    DESTDIR = $$PWD/../../lib/release
}

TEMPLATE = lib
CONFIG += dll

DEFINES += PROJECTLIB_LIBRARY

SOURCES += \
    class1.cpp \
    class2.cpp

HEADERS += \
    projectlib_global.h \
    class1.h \
    class2.h

La distinction debug/release n’est pas obligatoire, c’est une habitude de compilation sur plateforme Windows.

Le fichier commun "app.pri"

Comme nous avons plusieurs applications, l’application principale et les tests unitaires, nous allons écrire dans un seul fichier la configuration commune à toutes. Cela évite de dupliquer celle-ci dans chacun des fichiers .pro
fichier app.pri
PROJECTLIB = $$PWD/lib

CONFIG(release, debug|release) {
    LIBS += -L$$PROJECTLIB/release -lProjectLib
}

CONFIG(debug, debug|release){
    LIBS += -L$$PROJECTLIB/debug -lProjectLibd
}

INCLUDEPATH += $$PWD/src/lib

Pour que la librairie soit visible et accessible au niveau du linkage de l’application et des tests, on place celle ci dans le répertoire $$PWD/lib, qui se situe en dessous de la racine du projet. Au niveau de la librairie, cela est rendu possible grâce la variable DESTDIR (voir chapitre précédent sur la librairie).

 /projet
  |--- projet.pro
  |--- app.pri
  |--- /lib
  |    |--- /debug
  |    `--- /release
  `--- /src

La définition de la variable LIBS sera différente pour les autres cibles comme Linux ou MacOS. L’exemple s’adresse à un projet construit sur une plateforme Windows.
La définition de la variable INCLUDEPATH permet au compilateur d’aller trouver les fichiers entêtes de la librairie. Encore une fois, on va au plus simple dans cet exemple, il existe d’autres façons de faire, et chaque plateforme a ses spécificités.

L’application

C’est elle qui contient la fonction main principale et qui va construire l’application à partir des classes fournies par la libraire. Dans ce schéma, les tests unitaires se font sur les classes de la librairie et les tests fonctionnels seront réalisés sur l’application.
Le fichier .pro est relativement simple, on peut utiliser le wizard de Qt Creator pour créer une nouvelle application. L’instruction importante à retenir, c’est include(../../app.pri) qui va chercher la configuration commune pour linker correctement avec la librairie.
fichier app.pro
include(../../app.pri)


QT += core

TEMPLATE = app
TARGET = Project

SOURCES += main.cpp

Les Tests

Dans notre exemple, il y a deux tests, et chacun se comporte comme une application indépendant grâce à la macro QTEST_MAIN(TestClass) qui crée une méthode main()autonome et qui va exécuter les tests définis dans la classe passé en paramètre - voir Qt Documentation : Chapter 1: Writing a Unit Test sur l’écriture des tests unitaires.
On notera que la macro QTEST_MAIN() instancie un objet application de type QCoreApplication. Si ce n’est pas nécessaire, il est possible de faire appel à la macro QTEST_APPLESS_MAIN().

Le fichier .pro est aussi relativement simple, on peut utiliser le wizard de Qt Creator pour créer un nouveau test. Comme pour l’application, l’instruction importante à ne pas oublier, c’est include(../../../app.pri) qui va chercher la configuration commune pour linker correctement avec la librairie.
fichier tst_class1.pro
include (../../../app.pri)

QT += testlib
QT -= gui

CONFIG += qt console warn_on depend_includepath testcase
CONFIG -= app_bundle

TEMPLATE = app

SOURCES +=  tst_class1.cpp

Utiliser Qt Creator pour compiler et exécuter l’application et les tests

Depuis Qt Creator, ouvrir le fichier racine project.pro. Dans une arborescence unique du projet, on va pouvoir accéder aussi bien aux fichiers de l’application, des librairies et des tests.

 
  • Lancer qmake pour générer les Makefile.
  • Lancer la compilation globale du projet (Build All). Dans l’ordre c’est la librairie qui sera compilée en premier, l’application et les tests seront compilés par la suite (séquentiellement ou en parallèle).
  • Exécuter l’application ou les tests (en choisissant le type de run).

Conclusion

Voici les grands principes pour tout avoir dans un seul et même projet Qt. Il existe surement d’autres méthodes, mais l’avantage de ce système c’est cette intégration dynamique de l’application et des tests unitaires autour d’une ou plusieurs librairies. Toute modification de la librairie entraînera une recompilation automatique de l’application et des tests, ce qui garantira l’intégrité du projet.

Pour ce qui est de l’exécution des tests, il y a un nouveau système qui est apparu avec la version 4.3.0 de Qt Creator. Ce nouvel outil permet d’exécuter individuellement ou globalement tous les tests et fera l’objet d’un autre article.



Fin de l'article.

QML : user et abuser des types énumérés

Cette article tente de décrire les différents moyens d'exposer un type enum écrit en langage C++ dans le contexte du langage descriptif QML (Qt Modeling Language) de Qt. Travailler avec des types enums en QML présente de nombreux avantages :
  • une lecture explicite du code basée sur des types nommés plutôt que sur des chiffres sans signification précise.
  • une comparaison optimisée basée sur des valeurs entières plutôt que sur des chaînes de caractères.
  • le type énuméré est borné à un ensemble de valeurs constantes.
  • l'auto-complétion disponible dans Qt Creator, pour faciliter le choix de l'enum en QML.

Comment connecter la Wii-U à la Neufbox quand on a le code d'erreur 103-2001

Connecter la Wii-U à la Neufbox


Quand on déballe pour la première fois sa nouvelle console Wii-U et que, comme moi, vous avez une Neufbox de SFR pour assurez la connexion Internet, on est très vite confronté à un problème (surement un bug) qui fait échouer la connexion.

Si comme moi vous rencontrez cette erreur ésotérique de type 103-2001 dont le diagnostique n'a que peu de rapport avec la réalité, voici la procédure qui m'a permis de m'en sortir.