Accélérez vos tests Angular avec Ng-Bullet

Les équipes travaillant sur des projets d’une taille conséquente connaissent bien ce problème : la lenteur de l'exécution de tous nos tests et donc de nos builds.

Lors de ma dernière mission, lancer l’ensemble des tests Angular (+ de 3500) prenait environ une douzaine de minutes. Autant dire une éternité en informatique !
Et cela devenait problématique dans des cas d’urgence, de déploiements prioritaires, livraisons, etc…

Comment pouvait-on améliorer ceci rapidement et sans trop d’impact sur le projet ?

Découverte de la librairie Ng-Bullet

Nos tests utilisant le framework Jasmine et fortement TestBed d’Angular, nous nous sommes intéressés à Ng-Bullet.

C’est une bibliothèque qui vient surcharger la configuration Angular de chaque TestBed, pour éviter que le framework ne recompile le module cible à chaque test, ce qui a un coût important en terme de temps.
Pour plus de détails sur la mécanique et l’historique de cette librairie, je vous conseille l’article de son créateur.

Ce qui est intéressant à savoir, c’est que les changements à apporter sont minimes et répétitifs, mais nécessitent tout de même la modification de chaque fichier de test. Soit, dans notre cas, plus de 400 fichiers *.spec.ts, de cette manière :

bullet-changes

Pour cette tâche de refactoring, il est bien sûr conseillé de répartir la masse de modifications au sein des équipes/membres.
Cela permet à tout le monde de découvrir et mettre en place cette nouvelle syntaxe commune à quasiment tous les tests de composants.

Utilisation : gagnons du temps !

Ces modifications étant similaires à chaque fichier de tests, nous allons pouvoir faire un find/replace sur tous les fichiers de tests *.spec.ts, ce qui nous fera gagner un temps précieux :

find-replace
(car comme le disait Larry Wall, la paresse est une des vertus d’un programmeur.)

Pour cela, je vous ai créé 2 regex utilisables dans votre IDE préféré (pour moi IntelliJ) :

    1. Pour ajouter l’import ng-bullet et pour remplacer le beforeEach de configuration de module :

Recherche :

(^\A(.*)$)((\n.*)*)(beforeEach\(.*)((\n.*)*TestBed\.configureTestingModule)

Remplace :

import { configureTestSuite } from 'ng-bullet';\n$1$3configureTestSuite(() => {$6

    2. Pour retirer le .compileComponents() et une éventuelle parenthèse en trop :

Recherche :

(\n.*\.compileComponents\(\))(.*\n.*)}\)\)

Remplace :

$2})

Note : Il se peut que quelques uns de vos tests échouent (createComponent mal placé, Stub/Mock à réinitialiser, …). Il faudra donc, selon votre choix, soit passer manuellement sur chacun d’entre eux, soit laisser le test comme il était avant l’ajout de Ng-Bullet.


Conclusion

  • Cette librairie nous a-t-elle aidée dans notre recherche de rapidité d’exécution des TU ?

Oui.
Ayant testé Ng-Bullet sur 2 projets Angular, en modifiant simplement chaque Test Suite, sans recherche particulière d’optimisation, j’ai obtenu ces résultats :

results-bullet-1

Ce sont effectivement des résultats satisfaisants, le temps passé à effectuer ce refactoring sera largement récupéré par les gains sur les temps de builds/exécutions !

  • Peut-on encore améliorer ces temps ?

Bien sûr !
Plusieurs pistes s’offrent à nous :

  • Parallélisation des tests
  • Retirer les modules non-nécessaires dans chaque TestBed.configureTestingModule
  • Configurer différemment le compilateur
  • Abandonner Karma pour Jest (avec ici un article de mon collègue Corentin Bigot)