Il y a une semaine, Samori Gorce (@shinuza) est venu chez Valtech. L'objectif était de nous faire une démonstration inspirée par son dernier talk au ParisJS et de nous faire participer à une séance de TDD avec du code Javascript.
QUnit
Il a commencé par une présentation de QUnit. Ce framework de test est issu du fameux projet JQuery qui facilite les développements javascript dans le navigateur depuis déjà quelques années. Avec QUnit, on dispose d'une page web qui passe les tests. Il suffit d'importer sa librairie à tester et écrire les tests dans un autre script (exemple de source).
mocha
Mocha est un framework de tests unitaire qui tourne dans nodeJS et dans le navigateur. Nous avons eu l'occasion de le découvrir en détails pendant notre TP.
Tutorial
- Installation de la dernière version de node JS
Les paquets ubuntu de nodejs déploient une version un peu trop ancienne pour mocha. Il faut donc déclarer un repository particulier pour obtenir la dernière version avec le système de gestion de paquet du système:
$ sudo apt-get install python-software-properties $ sudo add-apt-repository ppa:chris-lea/node.js
Mise à jour et installation des paquets:$ sudo apt-get update $ sudo apt-get install nodejs $ sudo apt-get install npm
npm est le gestionnaire de dépendances de nodejs. Mocha est donc installé grâce à npm.$ sudo npm install mocha -g
Vérifications:$ node --version v0.6.11 $ npm --version 1.1.0-2 $ mocha --version 0.12.1
- Premier lancement
$ mkdir ~/mocha-tutorial/ $ cd ~/mocha-tutorial $ mocha node.js:201 throw e; // process.nextTick error, or 'error' event on first tick ^ Error: ENOENT, no such file or directory 'test' at Object.readdirSync (fs.js:390:18) at Object.
(/usr/lib/node_modules/mocha/bin/_mocha:174:14) at Module._compile (module.js:441:26) at Object..js (module.js:459:10) at Module.load (module.js:348:31) at Function._load (module.js:308:12) at Array.0 (module.js:479:10) at EventEmitter._tickCallback (node.js:192:40) - Créer la structure du projet
Mocha s'attend à trouver un répertoire test.
$ mkdir test $ mkdir lib # Répertoire de sources
- Premier test
var assert = require('assert'); require('../lib/array.js')(); describe('Array#prototype', function() { it('should return the first element of my array', function() { assert.equal([1, 3, 3, 4, 5].first(), 1); }); it('should return the last element of my array', function() { assert.equal([1, 3, 3, 4, 5].last(), 5); }); });
Dans ce premier fichier de test, on importe la librairie d'assertions de mochaassert
. On pourrait aussi utiliser une autre librairie d'assertions comme should.js par exemple.
Le reste du fichier de test est la spécification de deux méthodes qui doivent étendre le prototype de la classeArray
:first
etlast
.$ mocha node.js:201 throw e; // process.nextTick error, or 'error' event on first tick ^ Error: Cannot find module '../lib/array.js' at Function._resolveFilename (module.js:332:11) (...) at EventEmitter._tickCallback (node.js:192:40)
Comme on pouvait le prévoir, le test ne passe pas. - Première implémentation : étendre le prototype de la classe Array
Créez le fichier
array.js
dans le répertoirelib
module.exports = function() { Array.prototype.first = function() { return this[0]; }; Array.prototype.last = function() { return this[this.length - 1]; }; };
module.exports
fait partie de nodejs et permet d'isoler le contexte d’exécution dans un environnement restreint au test. C'est utile en particulier ici puisqu'on modifie le prototype de la classeArray
.$ mocha .. ✔ 2 tests complete (3ms)
Les deux points montrent ici qu'on a exécuté deux tests. Dans le cas de tests plus long, ils permettent de montrer l'avancement (comme dans phpunit et d'autres frameworks de test probablement). - Changer la sortie des tests
Mocha peut représenter le résultat des tests de différentes façons. Par exemple, on peut demander une sortie de type
spec
:$ mocha --reporter spec Array#prototype ✓ should return the first element of my array ✓ should return the last element of my array ✔ 2 tests complete (3ms)
Pour connaître la liste desreporters
disponibles :$ mocha --reporters dot - dot matrix doc - html documentation spec - hierarchical spec list json - single json object progress - progress bar list - spec-style listing tap - test-anything-protocol landing - unicode landing strip xunit - xunit reportert teamcity - teamcity ci support json-stream - newline delimited json events
Les connaisseurs auront tout de suite remarqué le reporterxunit
qui leur permettra d'obtenir le résultat des tests dans leur usine logicielle préférée. - Deuxième test : test d'une fonction asynchrone
Créez un fichier
hello.js
var hello = require('../lib/hello.js'); describe('Saying hello', function() { it('should wait before saying hello asynchronously', function(done) { hello(function() { done(); }); }); });
Il s'agit ici de spécifier une fonction asynchrone. - Implémentation de la fonction asynchrone
Créez un fichier
hello.js
dans le répertoirelib
:function hello(cb) { setTimeout(cb, 1000); } module.exports = hello;
Le résultat de ce test montre que le test asynchrone a pris une seconde pour s’exécuter :$ mocha -R spec Saying hello ✓ should wait before saying hello asynchronously (1004ms) Array#prototype ✓ should return the first element of my array ✓ should return the last element of my array ✔ 3 tests complete (1014ms)
Mocha trouve même ça bizarre et affiche ce temps. Dans la console, il colore même ce chiffre en rouge. - "Mocha is watching"
Il est possible de lancer Mocha en mode "watching". C'est-à-dire qu'il va inspecter les modifications dans le répertoire
tests
et repasser les tests en cas de modification. On a donc un feedback de test vraiment très rapide :$ mocha -w �? watching
On est proche du comportement d'un [infinitest](http://infinitest.github.com/) dans l'IDE Java par exemple.
D'autres frameworks
- [FuncUnit](http://funcunit.com/) Framework de test fonctionnel basé sur Selenium, JQuery, Qunit et EnvJS. Il Permet de rejouer des événements sur un site web dans un navigateur et dans l'usine logicielle grâce à EnvJS
- JS Test Driver Intégration d'un moteur d'éxécution sous forme d'un serveur dans l'IDE. Les clients sont les navigateurs qui se connectent au serveur pour attendre les tests à faire passer.
- [jsPerf](http://jsperf.com) Pour tester différentes implémentations de fonctions js dans des navigateurs différents.
- [Vows](http://vowsjs.org) Equivalent de mocha. Ajoute son propre langage d'assertions. Interface sympa.
- [JSCoverage](http://siliconforks.com/jscoverage) Couverture de code par les tests en javascript.
- Frameworks MVC : JavascriptMVC - EmberJS - **Backbone** - KnockoutJS
- **CasperJS** Fournit une API au dessus de [PhantomJS](http://www.phantomjs.org/) (navigateur headless) pour facilement scripter des tests fonctionnels
Conclusion
Résumé de la pile de test :
- Côté serveur : mocha est particulièrement adapté pour tester des implémentations serveur nodejs. mocha peut s'éxécute dans Jenkins
- Qunit pour les tests unitaires de composants IHM
- Tests fonctionnels : FuncUnit qui est basé sur Selenium tourne aussi dans Jenkins.
Limiter le “<acronym title=“Code souvent dupliqué qui n'apporte pas de valeur mais qui doit quand même être là pour que ça marche”>Boilerplate” pour passer les tests.