Tags

, , , , , , ,

Mardi, David Gageot a la dure tâche d’animer toute la soirée, dédiée aux tests et en particulier aux moyens d’accélérer l’exécution et le développement des tests. Voici un petit récapitulatif de la présentation de David.

Pourquoi rendre les tests plus rapides ? Pour accélérer le développement ! En gagnant du temps, je vais livrer plus souvent, vérifier la qualité plus souvent. L’intuition de David est que penser en avance à écrire des tests simples abouti à une application plus simple et de meilleure qualité. On suppose bien sûr que l’on es dans un contexte TDD où on lance les tests avant de commiter et qu’un serveur d’intégration continue build le projet et exécute les tests en continue.

Quoi ? tu testes pas ? t’es so 2010 ! On est en 2011, maintenant tu testes !

Dans ce contexte, s’il faut attendre des heures que les tests aient terminé de s’exécuter avant de pouvoir commiter alors la lenteur des tests devient un frein à la productivité. En plus, mécaniquement, chaque jour, la suite de tests est plus lente que la veille. Normal, le nombre de lignes de code et de lignes de tests augmente donc logiquement, ça prend plus de temps.

Donc, comment accélérer les tests ?

Facile, il suffit de déployer les tests sur plein de serveurs Hudson. Selon David, en fait non car le temps de build gagné va être perdu ailleurs, en maintenance de l’infra de cluster notamment. Selon moi, pour le moment peut-être, mais cela peut changer car des solutions de fermes de serveurs Hudson en SAAS comme la solution Dev@Cloud de CloudBees commencent à arriver donc, on pourra bientôt à moindre effort bénéficier d’une telle infrastructure de test sans les inconvénients de maintenance.

Il y a plusieurs façons de s’y prendre et David recensent trois profiles.

Le tricheur

Il achète une machine plus puissante. Idiot car les tests fonctionnent sur un seul thread. Maven 3 permet néanmoins de paralléliser les builds:

$ mvn -T4 clean install

Le paramêtre -T permet de configurer le nombre de workers, idéalement 1 à 2 par cœur. Selon David, le gain de temps est surtout visible sur les builds composés de nombreux modules. C’est en effet sur ce type de projet, que en mono threadé, il y a le plus d’attente, car les modules sont buildés de manière séquentielle et qu’en multi threadé, Maven aura plus d’opportunité de paralléliser.

Les utilisateurs de JUnit et de TestNG sont logés à la même enseigne. Maven Surefire permet maintenant de tirer aussi bien les tests Junit que les tests TestNG en parallèle.

Le faignant

Il supprime des tests pour réduire le temps de test. En effet, c’est radical, ça permet de gagner beaucoup de temps. Encore faut-il supprimer les tests inutiles ou les tests redondants ce qui demande évidemment beaucoup plus d’effort ! Mais cela fait aussi gagner beaucoup plus de temps.

Supprimer le code mort et les tests associés fait gagner un temps évident. Il est superflu de tester du code qui ne sert à rien !

Le courageux

Il travaille en sandbox et évite tout contact avec les systèmes connexes ! Il utilise une base de donnée mémoire comme HSQLDB ou mieux H2. Plus généralement, il évite tout IO (entrée/sortie) superflu, l’IO, c’est lent ! Les connexions réseau, c’est lent. Utiliser une BDD mémoire évite les aller/retour réseau. David utilise H2 chez Algodeal. Ils ont remarqué que H2 était très proche de MySQL, utilisé par ailleurs en production.

Bon évidemment, utiliser une base de donnée mémoire pose un problème avec les procédures stockées, impossible de les tester en dehors de leur base d’origine. Mais selon David, quand on rencontre un problème, on s’attaque au problème … comprendre qu’il vaut mieux éviter d’utiliser des procédures stockées. Je ne peux qu’acquiescer et c’est ce que j’essaye toujours de faire mais malheureusement, parfois, pour gagner en performance on n’a pas le choix. Dans ce cas là, on peut mocker.

Rendre testable une application existante est difficile. Le mieux, c’est, en amont, lors du choix des produits qui constituent l’architecture, de penser à sélectionner des produits qui fonctionnent in-process et sont test friendly, comprendre pensés pour être testable voir qui sont fournis avec un framework de test. C’est de plus en plus courant.

Pour tester l’envoie de mail, utiliser un serveur SMTP mémoire comme SubEtha SMTP. Il est autonome et utilisable en tests unitaire.

Pour éviter les IO FS (Système de Fichier), utiliser des API de FS virtuel comme Apache VFS ou Spring Resource. Mon préféré, c’est Apache VFS. Il y aussi JBoss VFS, le VFS introduit dans JBoss 5 mais utilisable en autonome aussi. Utiliser un VFS est difficile sur un projet sur lequel ça n’a pas été pensé dès le début, car les API de FS sont utilisées très bas dans les couches d’architecture. Néanmoins, utiliser un VFS procure un grand gain de performance en tests. Il permet en particulier de tirer de manière sûre des tests en parallèle sans risque des percutions sur les fichiers testés: un test crée un fichier pendant qu’un autre le supprime.

Ensuite, ne pas tester les règles métiers à travers toutes les couches. Pas la peine de tester une addition en passant par la couche IHM avec Selenium ! Mocker les couches inférieures des tests fonctionnels.

David n’aime pas l’AJAX : les utilisateurs en veulent, mais c’est compliqué à tester, à maintenir et on a pas envie de mettre en place une ferme de serveurs Selenium. David préfère le code server side qui fonctionne plutôt que du code client side qui plante. Le code server side est de surcroît plus facilement testable.

Pour finir, optimiser le code de l’application, les test n’en tourneront que plus vite !

C’est bien beau tout ça mais comment s’y prendre ? Et bien c’est simple, on prend le test le plus long, on l’optimise puis on recommence.

Les outils de test

Après avoir expliqué comment accélérer les test, David expose les outils qu’il utilise (ou pas) pour accélérer l’écriture des tests. L’objectif est aussi d’écrire des test plus expressifs, plus robustes et plus fiables.

MoreUnit

Facilite le refactoring de tests et donne des indicateurs sur les classes testées. Permet de naviguer entre test et code, de créer des tests, de lancer les tests avec des raccourcis. Aide au refactoring, renommage, déplacement des tests.

Infinitest

Après une modification de code, fait tourner automatiquement tous les tests qui dépendent du code modifié. Feedback extrêmement rapide sur les tests. Nécessite par contre que les tests soient très rapides pour être utilisable. Je l’ai testé sur Trans4j, un de mes pet projects, un petit framework de conversion de DTO, et c’est très efficace ! Le seul reproche est qu’on ne sais pas quels tests on été exécutés et comme l’a fait remarqué Antonio Goncalves, la vue de tests JUnit standard Eclipse peut ne pas être synchronisée avec Infinitest.

Junit Max

Outil similaire à Infinitest, créé par Kent Beck, l’inventeur du TDD. Tous les tests sont lancés à chaque modif. Priorise les tests les moins fiables et les plus rapides.

Hamcrest

API fluent d’assertions JUnit.

FEST-Assert

Autre API fluent d’assertion JUnit. Plus fluent que Hamcrest.

Junit 4

Partie dédiée aux nouveautés de JUnit 4.8. Certaines sont très utiles et peu connues.

Les @Rule. Permet de factoriser les actions de setup et teardown. L’API ExternalRessource permet de représenter des ressources qui necéssitent un setup et un teardown avant et après chaque test. Ex: connnection JDBC, serveur HTTP, …

TemporaryFolder est une @Rule qui permet de configurer une arbo de fichier temporaire ! Très pratique. Évidemment, c’est mieux d’utiliser une API de FS virtuel.

MethodRule API. Permet d’entourer une méthode de test avec un comportement commun.

Ex: La MethodRule Timeout permet de lever une exception de timeout quand une méthode de test prend trop de temps.

Tests sur Exception avec la @Rule ExpectedException
Là, je suis pas du tout convaincu, nécessite de déclarer la @Rule et de la configurer en début de test. Autant faire un try catch selon moi !

Les @Theory: permet d’exécuter les tests sur des ensembles de données de manière combinatoire. Je ne connais pas bien cette API mais j’ai pu utiliser une alternative, Twip très efficace et très simple à mettre en œuvre.

Fin

Cette soirée s’adressait aux personnes utilisant déjà les tests unitaires et confrontées au quotidien à des problématiques avancées de test unitaire. Les utilisateurs novices ou occasionnels avaient de quoi se sentir perdus au milieux de cette masse d’informations.

Je retiendrais pour ma part Infinitest que je ne connaissais pas et que j’ai déjà pu mettre en œuvre avec succès depuis le JUG. Je retiendrais aussi les @Rule de JUnit 4.8, je n’ai pas encore eu l’occasion de les utiliser mais je n’y manquerais pas dès que possible.