Le 11 janvier, à l’aube du 3ème anniversaire du Paris JUG, David Gageot a présenté, sous le titre “Comment j’ai mis ma suite de tests au régime en 5 minutes par jour”, son retour d’expérience sur les tests unitaires et sur la façon de les optimiser.

David prend une situation fictive mais probable :

Simon & Léa à la cafetéria d’entreprise :

  • Léa : « Encore en train de boire le café ? « 
  • Simon : « Oui, c’est mon ordinateur qui travaille pour moi en ce moment. »
  • Léa : « … »
  • Simon : « Je viens de corriger un bug majeur reproduit en production, j’ai lancé le build, les tests automatisés durent 20 heures et on pourra enfin mettre en production le correctif »
  • Léa : « Mais tu ne peux pas être prêt plus vite ? « 
  • Simon : « Si, mais sans exécuter les tests alors! « 

L’orateur nous présente 3 approches pour réduire la durée d’exécution des tests, le tout regroupé dans 3 profils de développeurs : les tricheurs, les fainéants et les courageux.

Le tricheur va :

  • acheter une machine plus puissante. Mais attention, une machine multi-coeurs ne va pas forcement être exploitée au maximum de ses capacités, voire, 1 seul coeur sera toujours utilisé
  • pour les projets mavenisés, paralléliser le build. Ainsi, la commande maven -T1 clean install utilisera 1 coeur, tandis que la commande maven -T4 clean install utilisera les 4 coeurs du processeur.
  • ou bien, pour paralléliser uniquement les tests, utiliser le plugin maven-surefire-plugi



Mais lors de la parallélisation des tests, le principe fondamental d’isolation entre les tests peut jouer des tours. Un test unitaire va lire un fichier, tandis qu’un autre va le supprimer. David Gageot recommande alors de mettre encore plus en évidence ces comportements en lançant une dizaine de threads et de corriger ces problèmes.

Le fainéant va :

  • intervenir sur le code source de façon à supprimer les tests “inutiles” ou les tests redondants, ou aller jusqu’à supprimer le code mort et les tests associés
  • Utiliser une base de données en mémoire au lieu de solliciter une base Oracle ou MySQL, même en local. La contrainte est que les fonctions testées ne doivent pas se reposer sur des procédures stockées
  • même avec l’avènement des bases NoSQL, il convient de s’assurer rapidement qu’il est aisé de monter et démonter une instance de base NoSQL en mémoire pour permettre l’exécution rapide de test (projet Voldemort par exemple)
  • mocker le serveur SMTP avec SubEtha SMTP par exemple. Cette librairie permet de mocker l’envoi de mails ainsi que de lister les e-mails envoyés, pratique lorsque lors de tests d’intégration où l’on souhaite tester que la routine de mail à la fin du formulaire d’inscription a bien envoyé un e-mail, par exemple.
  • le « un peu moins fainéant » aura prévu au début du projet l’utilisation d’un File System Virtualisé (VFS); Filesystem lui permetant de s’abstraire des accès disque (par exemple avec Apache VFS cf. section ram ou Spring resource)
  • ne pas lancer les tests pour un module qui n’a pas bougé. Aux risques et périls du développeur, mais envisageable pour peu que le projet soit bien découpé.

Et enfin, les courageux vont de façon générale intervenir plus en profondeur dans le code ou les tests :

  • par exemple, s’attaquer à un long test d’intégration et le remplacer par un test d’intégration plus petit et quelques tests unitaires (refactoring). Par exemple : dans le cas où les tests métiers sont réalisés dans les tests d’intégration, on peut espérer gagner beaucoup en rapatriant les tests métiers dans les tests unitaires
  • mocker certains composants ou services même lors des tests d’intégration
  • et enfin réduire les temps de traitement du code

En partant du principe que “Le cout de la complexité de l’application est payée à chaque fois que les tests sont lancés” – le projet à beaucoup à gagner à mettre sa suite de tests (unitaires et fonctionnels) au régime. (qui a parlé de Lean ? 😉

La présentation de David devrait être disponible sous peu à l’adresse : http://www.parisjug.org/xwiki/bin/view/Meeting/20110111