Au fil des flows

4avr/121

Ecrire plusieurs classes de tests dans un meme projet avec QT

Posted by Fabien Arcellier

J'aurai préféré posté la partie 2 de l'analyser d'equation mais j'ai un peu de retard dans sa mise en oeuvre. Je poste la résolution d'un problème que j'ai rencontré cette semaine avec Qt et la bibliotheque QTestLib.

Lorsque l'on crée un projet de test unitaire avec QT Creator, il crée une seule classe de test unitaire agrégée dans un fichier cpp.

Vue de QtCreator

Vue de QtCreator

A première vue, QT n'offre pas d'assistant pour créer de nouvelles classes de tests. C'est surprenant. Perdre cette dualité classe - classe de tests est un problème. En faite, si vous voulez composer une suite de tests unitaires comme vous feriez avec un framework de type xUnit, il faut apporter quelques modifications au projet. Pour cela commençons par comprendre comment est structuré le système.

Comment ça marche ?

Dans notre projet de test unitaire, il n'y a qu'un fichier cpp découpé en 3 parties :

  • la définition de la classe Modele1TestsTest
  • L'implémentation de la classe Modele1TestsTest
  • La macro QTEST_APPLESS_MAIN(Modele1TestsTest) + l'include du fichier moc

A première vue, le modele est proche de xUnit. On a une classe de test, une methode de type setup et teardown qui sont exécutés entre chaque test (respectivement initTestCase et cleanupTestCase).

Regardons de plus près la macro QTEST_APPLESS_MAIN. Vous trouverez sa définition dans les sources de QT dans le dossier src\testlib\qtest.h.

#define QTEST_APPLESS_MAIN(TestObject) \
int main(int argc, char *argv[]) \
{ \
    TestObject tc; \
    return QTest::qExec(&tc, argc, argv); \
}

Cette macro est simplement un raccourci pour déclarer un main. Je dois avouer que faire usage d'une macro pour ça est assez surprenant à mon goût. Pour économiser 5 lignes de code, on perd complètement la compréhension du système et donc l'impossibilité de l'étendre. Donc si on suit la logique, il suffit pour tester plusieurs classes de supprimer l'appel à cette macro et d'écrire une fonction main qui appelle les objets de tests un à un en utilisant QTEST::qExec(...).

Il reste l'include du fichier moc à comprendre. En faite, sa présence vient du fait qu'il n'y a pas de fichier header. Plus d'explications sont fournis dans la FAQ de developpez.com.

Si il y'avait un fichier header, on pourrait s'en passer. gMake se chargeant de faire le linkage.

Une demarche pour implementer une suite de tests

  • Creer un fichier main.cpp qui contient la fonction main pour appeller la suite de tests unitaires
  • Déplacer la définition de la classe Modele1TestsTest dans un fichier header
  • Supprimer La macro QTEST_APPLESS_MAIN(Modele1TestsTest) + l'include du fichier moc

Vous aurez des inclusions à faire pour que tout fonctionne mais rien de trop sorcier. A ce stade, vous avez le meme résultat que précedemment. Maintenant vous pouvez rajouter une classe de test supplémentaire.

Il suffira de créer un main de la forme suivante :

int main( int argc, char *argv[])
{
    Modele1TestsTest tc ;
    QTest::qExec (&tc, argc, argv );
    Modele2Tests tc2 ;
    QTest::qExec (&tc2, argc, argv );
    return 0;
}

A présent vous retrouvez le comportement plus habituel des frameworks de tests.

Remplis sous: Non classé 1 commentaire
18jan/125

InternalsVisibleTo, votre meilleur ami pour tester les methodes internes

Posted by Fabien Arcellier

J'arrive avec 4 ans de retard. Jusqu'à présent, j'ai toujours pu en C# tester plus ou moins le code de mes projets.
Seulement, pour me simplifier la vie, je déclarai la plupart de mes classes public. En terme d'encapsulation, vous en conviendrez c'est très moyen.

Aujourd'hui, j'ai rencontré le cas différent. L'assembly possède des classes qui ne doit pas être publique. Cependant je doit tout de même les tester.

Pour résoudre ce problème, j'ai découvert l'attribut InternalVisibleTo. Il permet de rendre accessible les methodes internes d'un assembly à un autre assembly. Ca ressemble beaucoup au mot clée friend en C++, qui s'applique lui aux classes. Pourquoi 4 ans de retard ? Tout simplement car cet attribut existe depuis dotNet 2.0.

Comment l'employer ?

En C#, vous pouvez placer ce fichier dans AssemblyInfo.cs. Voila ce que ça donne pour le programme ImageAnalyzer. Je veux que les methodes internes soient accessibles à l'assembly ImageAnalyzerTests.

[assembly: InternalsVisibleTo("ImageAnalyzerTests")]

Mais si quelqu'un appelle son assembly ImageAnalyzerTests ?

Effectivement, il pourra employer les methodes internes. Ue petite astuce. Vous pouvez executer vos tests unitaires en mode DEBUG.

Quand vous compilez en release, l'ouverture des methodes internes ne sera pas faite. Vous pouvez utiliser votre propre directive de compilation mais ça c'est une autre histoire.

#if DEBUG
    [assembly: InternalsVisibleTo("ImageAnalyzerTests")]
#endif

Voici 2 captures d'écran montrant les 2 scénarios.

InternalsVisibleTo Debug
InternalsVisibleTo Release

L'assembly de tests compilé ne peut plus atteindre les methodes de ImageAnalyzer compilé en Release

Autre point, vous pouvez aussi signer votre assembly et en tenir compte dans la declaration InternalsVisibleTo. Je vous renvoie à la MSDN pour plus de détails.

Remplis sous: Non classé 5 Commentaires