Intern.io – Retour sur la  » prochaine génération  » d’outils de tests Javascript

 » Tester, c’est douter  »

Abordez le sujet des tests logiciels dans n’importe quel bureau, et vous risquez d’entendre souvent cette phrase.

Chacun son point de vue, mais force est de constater que pour obtenir une application web avec un degré de fonctionnement optimal, la mise en place de tests qu’ils soient unitaires ou d’intégrations est obligatoire.

Je vais présenter tout au long de cet article un retour d’expérience sur une énième génération d’outils de tests Javascript que nous avons challengé en interne au sein de SII : intern.io

Les prérequis pour rejouer l’article de votre côté sont les suivants :

○ Node.js installé sur votre machine

○ cloner le dépôt git suivant qui contient l’ensemble du code utilisé et généré pour cet article: https://github.com/vogloblinsky/intern-feedback-helloworld

L’outil : intern.io

Ce « framework » est développé par la société américaine Sitepen au sein de son « laboratoire » depuis avril 2013, et est hébergé par la fondation Dojo.

Le positionnement est clair : face à la fragmentation des solutions existantes sur le marché, il est censé regrouper le meilleur de toutes ces solutions, et en ajouter d’autres également. C’est ce que nous allons voir dans la suite de cet article.

La solution est assez intéressante en découvrant le tableau de comparaison présent sur leur site :

Sélection_003

 

Tests unitaires, fonctionnels, support modules AMD, modules CommonJS, couverture de code native, intégration avec plusieurs solutions d’intégration continue, etc…

Je n’aborderai pas toutes ces possibilités, mais je vais juste l’intégrer dans un projet très simple, voir sa mise en place, et en faire un retour à chaud.

Projet support

Le projet support sera composé d’un module Javascript très simple, et d’une utilisation sommaire d’un framework SPA, AngularJS par exemple. L’idée est de se concentrer sur la partie « tests », et non la partie métier du code en lui-même.

Après clonage du dépôt git, voici la commande à réaliser dans votre terminal pour initialiser complètement le projet:

L’ouverture des dossiers et leur structure parle pour elle-même.

intern-feedback-helloworld-Sélection_001

Passons maintenant en revue rapidement les sources à tester.

Module vanilla js

Il s’agit tout simplement d’exposer 2 méthodes de conversion degré / Fahrenheit hébergées dans un module de conversion. Deux simples méthodes dans le scope global aurait suffi, mais anticipons plutôt la suite, si jamais nous devions exposer d’autres conversions plus tard.

Application AngularJS

Celle-ci va servir de couche de présentation pour notre module Javascript. Dans une simple page proposant 2 champs de saisie de type « Nombre », nous allons réaliser la conversion en dynamique grâce au data-binding interne des contrôleurs d’AngularJS.

Sélection_002

Nous avons donc une vue simple hébergée au sein même de la page index.html, et un contrôleur associé, ConverterCtrl. Celui-ci expose 2 modèles pour la vue, chacun portant la valeur de conversion suite à l’unité voulue; ces modèles étant ensuite reliés aux inputs.

Ensuite, 2 mécanismes d’écoutes « watchers » AngularJS permettent d’observer un quelconque changement sur les champs de saisie, un pour les degrés Celsius, un autre pour les degrés Fahrenheit.

Lorsque l’utilisateur change une valeur, un mécanisme de contrôle du champ est mis à jour pour ensuite bloquer l’écouteur inverse.

Exemple : l’utilisateur incrémente la valeur en Celsius. Nous précisons dans le contrôleur que c’est donc le champ ‘C’ qui est mis à jour. Dans l’écouteur du champ Fahrenheit, ne nous mettrons pas à jour la valeur Celsius. L’inverse est donc aussi valable.

Concernant l’utilisation du module de conversion, plusieurs choix peuvent être faits :

○ utiliser une directive créant chaque champ, paramétrable et s’occupant de la conversion de manière dynamique. Cette solution est propre car cela factorise le travail, mais peu rapidement être lourde à mettre en place.

○ j’ai préféré aller au plus simple en portant l’utilisation du module Vanilla JS dans un simple filtre AngularJS, utilisable soit dans la vue, soit dans le contrôleur après injection dans les dépendances.

Nous avons donc 2 fichiers à tester avec intern : le module Javascript, et le filtre AngularJS. Cela va permettre de comparer la mise en place et l’écriture des tests pour 2 structures de code différentes. Intern possède un dépôt Github avec bon nombre d’exemples avec d’autres framework : https://github.com/theintern/intern-examples

Intégration d’intern

Suite à l’installation plus haut des modules Node.js utilisés dans le projet, intern est disponible dans le dossier node_modules.

Remarquez que deux fichiers sont disponibles dans le dossier node_modules/.bin; ils servent de lien symbolique pour lancer l’outil en ligne de commande directe, ou alors en utilisant le gestionnaire de package de Node.js, npm.

Configuration

Comme tout outil, un petit fichier de configuration s’impose. Nous allons le mettre en place à la racine de notre dossier « test ».

En partant d’un modèle disponible dans le dossier du framework, nous avons déjà une base :

Je vous laisse lire toute la documentation sur ce fichier ici : Configuring intern , en voici une version simple pour les tests purs Vanilla JS :

Un deuxième fichier de configuration (intern.local) pour les tests locaux est disponible dans les sources et sert à spécifier le branchement sur un serveur local, en surchargeant le précédent fichier.

J’ai simplifié la liste des navigateurs utilisés, et précisé les sources à inclure dans la page de test, ainsi bien entendu que les 2 fichiers de tests. Venons-en à eux maintenant.

Ecriture d’un test

Le framework utilise la syntaxe AMD pour l’inclusion des fichiers dans le fichier de test. On approuve ou pas la syntaxe, en tout cas elle fait le boulot.

Nous allons écrire les tests en mode BDD, nous chargeons donc les librairies disponibles, Chai et le module bdd. Les autres styles d’écriture sont également disponibles : TDD, et objet.

Petite précision sur l’inclusion du module de conversion. Il est porté au sein d’une simple variable publique « temperatureConverter », et n’expose pas une api d’inclusion directe comme le sont les modules CommonJS par exemple (module.exports = {}). Nous l’appellerons donc simplement dans les tests avec son nom public.

Lancement d’un test

Intern possède deux types de « runners » : un client orienté tests navigateurs, un autre purement Javascript.

Nous allons donc tester ce petit module Javascript avec l’exécutable intern-client.

La commande de lancement des tests va être écrite dans le fichier package.json, elle sera ensuite accessible pour la partie intégration continue.

Ensuite une commande comme celle-ci lance les tests :

Avec comme résultat dans le terminal :

intern-feedback-helloworld-Sélection_012

Nous obtenons un tableau récapitulatif des taux de couverture pour le test qui vient d’être exécuté. C’est le rapport par défaut.

Le second test écrit dans le dossier ./test/browser concerne le filtre AngularJS, et j’ai également rajouté un test fonctionnel d’interaction avec les 2 inputs, situé dans ./test/functional.

Ils sont à appeler simplement avec l’autre runners orienté navigateurs : intern-runner.

Ensuite la commande suivante lancera les tests :

Il nous manque une chose importante pour lancer tout cela. Les tests orientés « fonctionnels » d’intern, c’est-à-dire « navigateurs », reposent sur l’utilisation du protocole standard WebDriver. Nous devons donc disposer d’une part d’un outil pour lancer les tests, et en plus d’un pilote pour le navigateur choisi, ici Chrome.

L’outil Selenium va servir de lanceur de tests, le pilote ChromeDriver complète le duo gagnant.

Je passe également l’installation de ces 2 outils, mais vous pouvez normalement simplement d’une part télécharger une version prête à l’emploi de Selenium-server, et ensuite installer le driver pour Chrome.

Lancer ensuite dans votre terminal le serveur que vous allez laisser tourner en tâche de fond.

Lancez ensuite la commande plus-haut, voici ensuite le résultat dans le terminal, entre temps une fenêtre Chrome s’est ouverte pour jouer les tests.

intern-feedback-helloworld-Sélection_002

Nous obtenons un tableau listant l’ensemble des fichiers testés et le rapport de taux de couverture.

Modifions 2 minutes les sources pour provoquer une erreur et observer un résultat différent.

intern-feedback-helloworld-Sélection_003

Les tests du module et donc par la suite du filtre AngularJS tombent.

Un autre point d’entrée plus visuel est disponible pour les tests fonctionnels. Il s’agit bien entendu d’une page HTML jouant les tests et disponible à l’adresse :

intern-feedback-helloworld-Sélection_004

Un retour est également disponible dans la console Javascript de votre navigateur.

intern-feedback-helloworld-Sélection_005

Le rapport de taux de couverture (basé sur Istanbul) peut aussi être généré lors des tests, sous plusieurs formats, cela s’avère très pratique pour une mise à disposition dans une plateforme d’intégration continue ou envoie sur un service web dédié (Coveralls.io par exemple).

intern-feedback-helloworld-Sélection_006

Branchement avec une plateforme d’intégration continue

Je vais parler ici rapidement du branchement avec Jenkins CI. Un autre branchement est disponible dans le projet source avec Travis CI. Les curieux trouveront leur bonheur ici : https://travis-ci.org/vogloblinsky/intern-feedback-helloworld

Pour lancer le build dans Jenkins, créer tout d’abord un nouveau job, et pointer ensuite sur votre dépôt local git ou celui du projet sur Github.

intern-feedback-helloworld-Sélection_007

puis configurer le job pour lancer une tâche shell :

intern-feedback-helloworld-Sélection_008

Petite remarque concernant Node.js et Jenkins. J’ai fait le choix ici étant sur ma machine d’utiliser la version de Node.js installée en local. Sur un vrai serveur Jenkins, ce choix peut aussi être fait, ou alors vous pouvez utiliser le plugin Jenkins Node.js. De la littérature existe pour cela : johnhamelink.com, dylants.com

Pour ma part le plugin Node.js est installé, mais pas utilisé ici dans ce job, la case étant décochée dans la capture ci-dessus.

Autre point concernant la partie Selenium, je pars du principe qu’en lançant un job sur votre serveur Jenkins local, votre Selenium-server-standalone de tout-à-l’heure tourne toujours. Si un serveur dédié sans interface graphique, de la littérature existe aussi : Lien

Le résultat reste le même que pour les autres lancements:

intern-feedback-helloworld-Sélection_009

Intern possède un certain nombre de format de rapports concernant la partie tests : junit, lcov, lcovhtml, teamcity, etc…

Leur activation se fait dans la ligne de commande lancée par npm, lançons par exemple un export junit en plus de l’export Istanbul Html vu plus haut :

En activant dans la configuration du job Jenkins l’export vers Junit, nous obtenons ce type de graphique dans la page d’accueil du job après 2 builds successifs :

intern-feedback-helloworld-Sélection_010

Comparaison simple avec Karma+Jasmine 2

J’ai réalisé le même test du module Vanilla JS avec le lanceur de tests Karma et le framework de tests Jasmine, situé dans ./test-karma

Le fichier Jasmine est globalement plus léger qu’Intern, mais la syntaxe de définition d’intern sépare clairement bien les choses : un tableau pour les tests unitaires, un autre pour les tests fonctionnels, et un dernier pour les sources.

De plus, l’installation de Karma a nécessité 5 autres plugins liés pour arriver au même résultat qu’intern : karma-chrome-launcher, karma-coverage, karma-jasmine, karma-junit-reporter, karma-story-reporter.

Conclusion

La promesse abordée en début d’article de regrouper le meilleur des différentes solutions du marché est là. La solution est assez simple à mettre en place et a l’avantage de tout comporter en son sein.

Les avantages sont pour moi purement « pratiques », le fond du sujet, c’est-à-dire les tests, produisent les mêmes résultats. L’argument marketing concernant la « productivité » sera aussi bien voir mieux entendu qu’un aspect purement technique pour la plupart des développeurs; il est toujours plus intéressant de perdre du temps sur l’écriture de tests que sur de la configuration d’outils.

Mais comme toujours, rien n’est parfait, voici pour ma part quelques points négatifs à prendre en compte avant de choisir cette solution :

○ il manque de la configuration pour la partie export des rapports, les rapports sont par défaut à la racine du projet. Des solutions temporaires pas très propres existent : Lien

○ aucun plugin gulp disponible, une version grunt existe et est fournie par Intern. Ce manque est facilement comblé avec le package gulp-shell et une tâche gulp de ce type :

○ l’intégration d’une solution comme Plato aurait aussi été intéressante. Cependant les deux peuvent être lancés séquentiellement avec un outil comme gulp ou grunt.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

19 + onze =