I. Avant-propos

Dans le précédent tutoriel, nous avons modifié l'application pour prendre en compte plusieurs flux.

Cette fois-ci, vous apprendrez comment écrire un petit jeu de tests pour tester les principaux contrôleurs de l'application. Il s'agira de faire des données de simulation (mocking data).

Vous pouvez obtenir la source sur alexyoung / djsreader en version 7b4bda.

II. Tests propres et bien organisés

Le but de ce tutoriel est de montrer une seule méthode pour écrire des tests propres et bien organisés. Idéalement les données à tester devraient être stockées dans des fichiers séparés et chargées lorsque nécessaires. Nous ne voulons absolument pas que des variables globales encombrent la mémoire.

Pour exécuter des tests avec l'application générée par Yeoman, sur laquelle nous avons travaillée, tapez grunt test. Ça va utiliser KarmaKarma et JasmineJasmine pour exécuter des tests par le biais de Chrome à l'aide de WebSockets. Le workflow dans la console se fait sans effort, quoique Chrome apparaisse et disparaisse en fond (ça ne perturbera pas votre session existante de Chrome, ça fera un processus distinct). Il ne récupère pas le focus non plus, ce qui signifie que vous pouvez invoquer les tests et continuer à travailler sur le code sans vous faire interrompre.

Image non disponible

L'approche de base consiste à utiliser $httpBackend.whenJSONP pour dire à AngularJS de renvoyer des données de simulation lorsque les tests sont exécutés, au lieu d'aller chercher les données des flux réels de Yahoo!. Cela semble assez simple, mais il y a une légère compilation : laisser les données de tests dans la partie test ne convient pas. Alors, que faisons-nous à ce sujet ? Le fichier karma.conf.js a été créé pour nous par le générateur Yeoman et contient une ligne pour charger des fichiers d'un répertoire de données de simulation : 'test/mock/**/*.js. Elles seront chargées avant les tests. Nous allons donc jeter quelques JSON là-dedans.

Fait intéressant, si vous exécutez grunt test dès maintenant, il échouera parce que l'application effectue une demande de JSONP et la bibliothèque de simulation d'AngularJS signalera cela comme une erreur. $httpBackend.whenJSONP va résoudre le problème.

III. Les JSON de simulation

Ouvrez un fichier appelé test/mock/feed.js (vous devez au préalable exécuter la commande mkdir test/mock). Ensuite ajoutez-y ceci :

 
Sélectionnez
'use strict';

angular.module('mockedFeed', [])
  .value('defaultJSON', {
    query: {
      count: 2,
      created: '2013-05-16T15:01:31Z',
      lang: 'en-US',
      results: {
        entry: [
          {
            title: 'Node Roundup: 0.11.2, 0.10.6, subscribe, Omelette',
            link: { href: 'http://dailyjs.com/2013/05/15/node-roundup' },
            updated: '2013-05-15T00:00:00+01:00',
            id: 'http://dailyjs.com/2013/05/15/node-roundup',
            content: { type: 'html', content: 'example' }
          },
          {
            title: 'jQuery Roundup: 1.10, jquery-markup, zelect',
            link: { href: 'http://dailyjs.com/2013/05/14/jquery-roundup' },
            updated: '2013-05-14T00:00:00+01:00',
            id: 'http://dailyjs.com/2013/05/14/jquery-roundup',
            content: { type: 'html', content: 'example 2' }
          }
        ]
      }
    }
  });

Cet exemple utilise angular.module().value pour définir une valeur qui contient quelques JSON. J'ai dérivé ce JSON de l'API de Yahoo! en exécutant l'application et en regardant le trafic réseau dans le WebKit Inspector, puis j'ai édité les propriétés content parce qu'elles étaient énormes (DailyJS met les articles complets dans le flux).

IV. Charger les données de simulation

Ouvrez le fichier test/spec/controllers/main.js et changer le premier beforeEach afin de charger mockedFeed :

 
Sélectionnez
beforeEach(module('djsreaderApp', 'mockedFeed'));

La méthode beforeEach est fournie par Jasmine et exécutera la fonction spécifiée avant chaque test. Maintenant la valeur de defaultJSON peut être injectée, ainsi que le retour HTTP :

 
Sélectionnez
var MainCtrl, scope, mockedFeed, httpBackend;

// Initialise le contrôleur et une zone de test
beforeEach(inject(function($controller, $rootScope, $httpBackend, defaultJSON) {
  // Met en place le flux
  httpBackend = $httpBackend;
  $httpBackend.whenJSONP(/query.yahooapis.com/).respond(defaultJSON);

  scope = $rootScope.$new();
  MainCtrl = $controller('MainCtrl', {
    $scope: scope
  });
}));

Vous devriez être capable de deviner ce qui se passe avec $httpBackend.whenJSONP(/query.yahooapis.com/). Chaque fois que l'application tente de contacter le service de Yahoo!, il va déclencher notre retour HTTP de simulation et retournez la valeur defaultJSON à la place. Cool !

V. Le test

Le test proprement dit est tout à fait ridicule après tout ce que l'on vient de faire :

 
Sélectionnez
it('devrait avoir une liste de flux', function() {
  expect(scope.feeds.length).toBe(1);
  httpBackend.flush();
  expect(scope.feeds[0].items[0].title).toBe('Node Roundup: 0.11.2, 0.10.6, subscribe, Omelette');
});

Le test vérifie que $scope a les données attendues. httpBackend.flush fera en sorte que la (fausse) requête HTTP soit terminée la première. La valeur de scope.feeds est celle qui dérive de MainCtrl (vu dans le précédent article) provenant du JSON retourné par Yahoo!.

VI. Conclusion

Vous devriez maintenant être en mesure d'exécuter grunt test et de voir quelques tests se dérouler (comme sur ma capture d'écran). Si ce n'est pas le cas, consultez djsreader sur GitHub pour voir ce qui est différent.

La version pour ce tutoriel est la 7b4bda.

VII. Remerciements

L'article original peut être lu sur le site DailyJSDailyJS : AngularJS: TestsAngularJS: Tests.

Je remercie égalementzoom61 pour sa relecture attentive et assidue.