Accueil > Web > Tester le front-end JavaScript d’une application web

Tester le front-end JavaScript d’une application web

Introduction

La mise en place des tests automatiques permet de s’assurer du bon fonctionnement et de la conformité au cahier des charges d’une application. Beaucoup d’équipes de développement ont intégré des processus d’automatisation des tests au sein de la stratégie d’intégration continue.

Le back-end est systématiquement soumis à des suites de tests unitaires, mais qu’en est-il des tests du front-end ?

Souvent délaissé, on ne le teste que très peu, voire pas du tout. Seuls les tests manuels sont mis en place : on ouvre le navigateur et on vérifie que tout marche correctement.

Ce genre de tests ne permet pas de vérifier la majeure partie du code rédigé car ils se concentrent uniquement sur le fonctionnel et le visuel.

Mettre en place des tests du front-end structurés, par exemple en utilisant des mock objects (simulation des réponses d’une API REST du back-end), permet également de développer l’interface graphique sans avoir à attendre un back-end fonctionnel : c’est plus efficient.

Enfin, avec l’arrivée des nouveaux frameworks de front-end full-javascript (Angular, Backbone, Ember, et j’en passe), on crée des modules réutilisables, qu’il convient de valider et de tester afin de mesurer leur stabilité, et donc leur ré-utilisabilité dans d’autres projets… tout comme on le ferait pour un module Node.js !

Les bonnes pratiques

Comment tester efficacement le front-end JavaScript d’une application web ?

Faire et tester une application web, c’est un peu comme cuisiner.

Puisqu’une image vaut plus que des mots, voici la célèbre pyramide des tests de Mike Cohn :

Pyramide des tests

Commençons par les tests unitaires. Les tests unitaires permettent de tester chaque fonction du code javascript individuellement. La bonne pratique veut qu’après avoir écrit une fonction, on rédige un ou plusieurs tests unitaires qui permettent de valider le comportement de la fonction, même en lui passant des paramètres qui ne seront probablement pas passés lors de l’exécution de l’application (null, undefined, infini, zéro, objets, etc…). Cela permet de valider la solidité individuelle de la fonction.

Ils constituent la base de la pyramide, car ce sont des tests qui sont peu coûteux à rédiger (en temps), et qu’ils apportent un retour fiable et rapide sur la qualité du code (« ce test a échoué, il correspond à la règle de gestion X dans le fichier Y »). Ils sont efficaces, car ils permettent d’isoler une erreur en quelques secondes, et de la corriger rapidement.

De par leur fiabilité et leur efficacité, les tests unitaires doivent représenter environ 80%1 des tests du front-end d’une application web.

Cookie-Ingredients.jpg

Ces tests permettent de s’assurer que nous cuisinons avec des ingrédients de bonne qualité.

Ensuite, on trouve les tests d’intégration / services. Une fois qu’on a vérifié que toutes nos classes, contrôleurs, filtres, et autres, fonctionnent comme attendu, il s’agit de vérifier que tous ces éléments peuvent marcher ensemble, et que ce qu’ils font ensemble est bien ce à quoi on s’attend. On retrouvera par exemple les tests d’API.

Les tests d’intégration doivent représenter environ 10%1 des tests du front-end.

CDC-Releases-Cookie-Dough-E-Coli-Outbreak-Statistics.jpg

On met le doigt dans la pâte pour vérifier que ce que nous sommes en train de préparer a bon goût

Et pour finir, le dernier niveau de tests automatiques : les tests fonctionnels. Ils sont là pour valider ce que le client va voir. Ce sont des tests longs à écrire et qui apportent peu de retours utiles côté technique (on a beau savoir que ça ne marche pas comme prévu, on ne sait pas où modifier le code pour corriger l’erreur). C’est pour cela qu’on essaie d’en rédiger le moins possible.

Les tests fonctionnels doivent représenter environ 5%1 des tests du front-end.

beautiful-cookie-food-girl-love-Favim.com-333176.jpg

On vérifie que notre bon petit plat ne ressemble pas à une bouillie sans forme.

Pour terminer, au sommet de la pyramide, on trouve les tests manuels. Ils doivent servir le moins possible à valider le point de vue fonctionnel de l’application, qui doit être assuré par les tests unitaires et fonctionnels.

Les tests manuels se concentrent surtout sur l’expérience utilisateur de l’application. Il est conseillé de faire tester son application à quelqu’un d’extérieur au projet afin de lever les points qui peuvent paraître évidents pour l’équipe, mais pas aux utilisateurs, et ainsi améliorer l’ergonomie de l’interface.

Une petite bouchée pour goûter…

Les frameworks existants

Il existe une multitude de frameworks de tests, je n’en cite ici que quelques uns que nous avons l’habitude d’utiliser au sein de l’équipe, et ceux que je préfère utiliser…

Tests unitaires et d’intégration

Mocha

Mocha est un framework de lancement de tests exécutable par commande Node.js ou bien directement intégrable comme <script> dans un navigateur.

Avantages :
  • Fonctionne avec n’importe quelle librairie d’assertions (assert de Node.js, expect, should, …), ce qui permet une grande liberté sur l’écriture des tests.

  • Les tests sur les fonctions asynchrones sont faciles à écrire.

  • Permet d’intégrer une bibliothèque pour simuler un environnement (sandbox) afin de tester les fonctions indépendamment de leur contexte, sinon.js.

  • Permet d’intégrer une bibliothèque de test coverage (pourcentage de code de l’application validé par des tests unitaires), par exemple istanbul.

Inconvénients :
  • Mocha est flexible, mais il n’est pas “packagé” de base, et il faut assembler nous même les librairies que l’on souhaite utiliser, ce qui peut être assez compliqué pour une première utilisation. Choisir quelles librairies utiliser peut également prendre du temps.

  • Pas de bibliothèque de spy (quelle fonction a été appellée, et combien de fois ? Qu’a-t-elle retourné ?) par défaut, il faut utiliser sinon.js.

  • Pas de bibliothèque d’assertion de base, il faut utiliser, par exemple, chai (regroupe les librairies expect, assert et should).

Exemple de code :

Cet exemple de code utilise Chai, une librairie regroupant plusieurs librairies d’assertions donnant accès à should, expect, et assert. J’ai également intégré sinon.js pour donner un exemple de simulation d’environnement et de spy.

Jasmine

Jasmine est un framework de test de code Javascript qui permet de définir des suites de tests, de créer des assertions, de créer des spy, etc. La majeure différence entre Jasmine et Mocha, c’est que Jasmine est tout-en-un : il n’y a qu’une librairie à installer et on peut tout faire avec, mais c’est moins flexible (notamment sur la syntaxe).

Tests fonctionnels

Selenium WebDriver

Selenium lance des tests sur une application en utilisant un vrai navigateur, en interagissant avec celui-ci comme le ferait un utilisateur. Il est possible de créer des enregistrements directement depuis l’interface graphique d’un navigateur, ou de rédiger entièrement avec du code les différents tests à exécuter.

Protractor

Protractor est la librairie d’AngularJS basée sur WebDriver.

PhantomJS + suite de tests au choix

PhantomJS est un navigateur sans interface graphique. Il n’est pas livré avec une librairie de tests, c’est juste un navigateur, qu’on lance en ligne de commande. Utilisé avec une librairie de tests intégrée au navigateur (par exemple, Mocha), il est possible d’effectuer des tests fonctionnels.

CasperJS

CasperJS est basé sur PhantomJS (moteur de rendu Webkit) et sur SlimerJS (l’équivalent de PhantomJS mais avec un moteur de rendu Gecko). Il propose en plus une API complète de tests.

Exemple d’application

Pour reprendre un exemple d’application simple, imaginons une calculatrice.

Les tests unitaires consisteraient à tester les opérateurs (addition, soustraction, multiplication, division).

Les tests d’intégration viseraient à tester le regroupement de ces opérateurs (chaînage d’opérations (3+5*7)).

Les tests fonctionnels serviraient à tester l’interface graphique (simulation d’entrée clavier dans les champs de texte et clic sur le bouton calculer).

En pratique sur un projet de petite/moyenne taille, les tests fonctionnels (GUI) peuvent être négligés puisqu’ils sont longs à écrire et qu’ils ne rapportent que peu de retours techniques. En revanche, si le projet prend de l’ampleur et que vous commencez à mettre en place une stratégie d’intégration continue (lancements automatiques des tests lors d’un push sur une branche de dépôt git, par exemple), il devient intéressant, voire nécessaire (pour ne pas dire obligatoire), d’en rédiger.

Conclusion

Le front-end d’une application doit lui aussi être testé de façon unitaire, en particulier les éléments réutilisables (si vous écrivez une classe, un service AngularJS, ou autres). Tester ces éléments du front-end permet de s’assurer de leur bon fonctionnement et de leur autonomie, tout en fournissant aux développeurs qui vont réutiliser ce composant un exemple d’utilisation de votre composant (car pour le tester, il faut l’utiliser !).

Notes

: Ces chiffres ne sont pas gravés dans le marbre mais fournissent un bon indicatif des proportions, comme l’explique Bas sur On Test Automation

Références

  1. Pas encore de commentaire
  1. Pas encore de trackbacks


huit × 4 =