Récemment j'ai eu besoin d'avoir du code coverage (assuré par istanbul) pour des tests d'intégration d'une
application AngularJS. Ces tests sont lancés avec protractor et ce n'est pas aussi simple qu'il n'y
parait. L'idée de base est :
- D'associer une fonction à la clé onPrepare dans la configuration de
protractor. Dans cette fonction on définit un rapporteur qui va pour chaque
test collecter le code coverage.
- D'utiliser un plugin (l'idée initiale est de Jeffrey Barrus) afin d'attendre que
le rapport soit écrit (via une promise). En effet, protractor dispose
d'une option onComplete qui est une fonction appelée lorsque les tests
sont terminés. Cependant, cette fonction ne supporte pas l'asynchronisme :
elle ne supporte pas les callbacks et ne va pas attendre qu'une promise soit
résolue. Par conséquent, il est possible que le processus de protractor se
termine avant que le rapport ne soit écrit ou que istanbul n'ait reçu les
données de coverage. Heureusement, la méthode tearDown d'un plugin peut
retourner une promise et protractor attendra sa résolution avant de quitter.
Je suppose que vous savez comment préparer vos fichiers javascript pour istanbul
(étape appelée instrumentation). Si ce n'est pas le cas, vous pouvez lire la
doc.
Les configurations sont les suivantes (disponibles en téléchargement en fin
d'article) :
1 var istanbul = require('istanbul');
2 var collector = new istanbul.Collector();
3 var reporter;
4 var waitPlugin = require('./waitPlugin');
5
6 function report() {
7 if (reporter) {
8 reporter.add('json');
9 reporter.write(collector, true, function () {
10 console.log('Coverage report successfully written');
11 });
12 }
13 }
14
15 exports.config = {
16 specs: [
17 '../test/integration/*.spec.js',
18 '../test/selenium/*_test.js'
19 ],
20 seleniumAddress: '${seleniumAddress}',
21 maxSessions: 1,
22 multiCapabilities: [
23 {browserName: 'firefox', shardTestFiles: true, maxInstances: 1}
24 ],
25 framework: 'jasmine2',
26 plugins: [{path: './waitPlugin.js'}],
27 onPrepare: function () {
28 var jasmineEnv = jasmine.getEnv();
29 waitPlugin.setOnComplete(report);
30 browser.driver.manage().window().maximize();
31 browser.get('${testPortalAddress}');
32
33 jasmineEnv.addReporter(new function () {
34 this.specDone = function (spec) {
35 if (spec.status !== 'failed') {
36 var name = spec.fullName.replace(/ /g, '_');
37 var reportfile = 'coverage/integration/json/' + name;
38 reporter = new istanbul.Reporter(undefined, reportfile);
39 var promise = browser.driver.executeScript('return __coverage__;')
40 .then(function (coverageResults) {
41 collector.add(coverageResults);
42 });
43 waitPlugin.waitList.push(promise);
44 }
45 };
46 });
47 }
48 };
- On commence par charger les modules (istanbul, le collecteur, le plugin). On
crée également la variable reporter. Elle contiendra par la suite un
objet utilisé dans différentes fonctions.
- Ensuite on crée la fonction report qui sera utilisé par la suite pour
écrire le rapport. Pour cela, on donne le format du rapport (ici
json). Plusieurs formats sont supportés et istanbul peut générer un rapport
dans plusieurs formats à la fois.
- Dans la configuration de protractor, rien de particulier jusqu'à la ligne 27
pour la fonction onPrepare.
- À la ligne 29, on définit la fonction à appeler une fois que toutes les
promises du plugin « attendre » sont résolues, en l'occurrence notre
fonction report.
- À la ligne 33, on ajoute un rapporteur à jasmine, le framework de
tests. Ce rapporteur comprend une méthode specDone qui est appelée
(comme son nom l'indique) à chaque fois qu'un spec est terminé. Si les
tests ont réussi, on récupère le code coverage depuis la variable globale
__coverage__ de istanbul et on ajoute le résultat au collecteur. Ces
opérations étant asynchrone et renvoyant une promise, on ajoute cette
dernière à la liste de promises à attendre dans notre plugin.
Pour le plugin wait, rien de très compliqué, je pense que le code parle de lui
même. Pensez simplement à installer la bibliothèque q dont dépend le script
avec npm install q ou npm install --save q pour ajouter q à votre
package.json. En cas de problèmes, vous pouvez laisser un commentaire.
1 var q = require('q');
2
3 var waitList = [];
4 var onComplete;
5
6 exports.waitList = waitList;
7
8 exports.setOnComplete = function(cb) {
9 onComplete = cb;
10 };
11
12 exports.teardown = function () {
13 return q.all(waitList)
14 .then(function() {
15 if (onComplete) {
16 return onComplete();
17 }
18 });
19 };
Si comme moi, vous devez lancer chaque fichier spec dans un nouveau navigateur,
pour avoir la couverture globale, vous devez écrire le résultat de chaque jeu de
tests dans un fichier json puis rassembler les résultats (par exemple en un
rapport html plus lisible) avec la commande suivante :
istanbul report --include 'coverage/integration/json/**/*.json' --dir 'coverage/integration' html
Si en revanche vous pouvez lancer tous les tests dans un seul navigateur, vous
pouvez récupérer le rapport au format HTML directement en changeant ligne 8 de
la configuration de protractor json en html.
Téléchargements
À réutiliser sans modération !