Nous sommes maintenant convaincus que l’écriture des tests est un élément essentiel à la qualité logicielle. La méthodologie agile en fait aussi un élément important du succès d’un projet.
Dans cet article, nous verrons comment vérifier que les appels GET à un API REST sont bien exécutés et comment simuler un serveur API REST grâce au service $httpBackend offert par AngularJS.
$httpBackend pour les tests unitaires
L’avantage d’utiliser $httpBackend est qu’il n’est pas nécessaire de mettre en place un vrai serveur API Rest. Normalement, il faudrait mettre en place un API Rest fonctionnel, y injecter des données de test (fixtures) et ensuite faire un appel Ajax à cet API, attendre la réponse et vérifier les résultats obtenus.
Mettre en place un environnement de test complet demande un effort considérable et ajoute une dépendance externe à notre projet complexifiant ainsi les tests automatisés.
Afin de simplifier et d’accélérer les tests unitaires, il sera possible de les exécuter directement dans le navigateur grâce au service $httpBackend.
$httpBackend
$htpBackend simule (fake) un serveur HTTP. Il est utilisé dans les tests unitaires afin de vérifier le comportement du service $http. $httpBackend est un service de ngMock.
L’objectif est d’exécuter des tests rapidement sans avoir à envoyer de vraies requêtes AJAX (XHR) à un vrai serveur. Dans ces tests, nous vérifieront que les bonnes requêtes sont envoyées ou non. Il sera possible de répondre aux requêtes grâce à des réponses statiques ou dynamiques et finalement de vérifier les résultats attendus.
Deux options complémentaires sont offertes pour réaliser les tests : expect et when.
$httpBackend.expect – Request expectation
Une méthode qui défini les requêtes qui devraient être exécutées par l’application.
$httpBackend.when – Backend definition
Une méthode qui défini ce que doit retourner le fake API REST lorsqu’une requête est envoyée par l’application
Tableau comparatif entre expect() et when()
Le tableau suivant présente les différences entre les deux méthodes :
Request expectations |
Backend definitions |
|
---|---|---|
Syntaxe |
.expect(…).respond(…) | .when(…).respond(…) |
Usage typique |
Tests unitaires stricte | Tests unitaires (black-box) |
Complète plusieurs requêtes |
NON | OUI |
L’ordre des requêtes est important |
OUI | NON |
Requête requise |
OUI | NON |
Réponse requise |
Optionnel | OUI |
Tiré de la documentation AngularJS – $httpBackend
https://docs.angularjs.org/api/ngMock/service/$httpBackend
Exemple de code avec $httpBackend.expectGET
Dans l’exemple suivant, on vérifie que le contrôleur appelle bien l’URL /auth avec une requête GET. Les méthodes expectGET et flush du service $httpBackend sont utilisées :
1 2 3 4 5 6 7 8 9 10 |
it('should fetch authentication token', function() { // expect - Indique que l'URL /auth doit être appelé en mode GET $httpBackend.expectGET('/auth'); // Le contrôleur est initialisé (il devrait appeler /auth) var controller = createController(); // Les requêtes latentes sont retournées $httpBackend.flush(); }); |
Dans l’exemple précédent, si le contrôleur ne fait pas pas d’appel GET à l’adresse /auth, le test échouera suite à l’appel flush().
Exemple de code avec $httpBackend.when
Dans l’exemple suivant, une réponse à retourner est configurée avec when. Donc, quand l’URL /auth est appelé une réponse 401 (401 est le code HTTP pour Unauthorized) est retournée.
1 2 3 4 5 6 7 |
it('should fail authentication', function() { $httpBackend.when('GET', '/auth').respond(401, ''); $httpBackend.expectGET('/auth'); var controller = createController(); $httpBackend.flush(); expect($rootScope.status).toBe('Failed...'); }); |
Injection des dépendances
Afin d’utiliser $httpBackend dans vos tests unitaires, vous devez l’injecter. Plusieurs façon d’injecter les dépendances sont disponibles. Dans l’exemple suivant, le service $injector est utilisé.
https://docs.angularjs.org/api/auto/service/$injector
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
describe('MyController', function() { var $httpBackend, $rootScope, createController, authRequestHandler; beforeEach(module('MyApp')); beforeEach(inject(function($injector) { // Récupère le service $httpBackend $httpBackend = $injector.get('$httpBackend'); // Confirmation commune du when pour nos tests authRequestHandler = $httpBackend.when('GET', '/auth') .respond({userId: 'userX'}, {'A-Token': 'xxx'}); $rootScope = $injector.get('$rootScope'); var $controller = $injector.get('$controller'); createController = function() { return $controller('MyController', {'$scope' : $rootScope }); }; })); |
Requêtes restantes?
Après chaque test il est possible de vérifier s’il existe des requêtes latentes (non exécutées) ou si des vérification (expect) n’ont pas été réalisées. Les méthodes verifyNoOutstandingExpectation et verifyNoOutstandingRequest sont ajoutés à la fonction afterEach, exécutée automatiquement après chaque test.
1 2 3 4 |
afterEach(function() { $httpBackend.verifyNoOutstandingExpectation(); $httpBackend.verifyNoOutstandingRequest(); }); |
Exemple complet
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
describe('MyController', function() { var $httpBackend, $rootScope, createController, authRequestHandler; beforeEach(module('MyApp')); beforeEach(inject(function($injector) { $httpBackend = $injector.get('$httpBackend'); authRequestHandler = $httpBackend.when('GET', '/auth') .respond({userId: 'userX'}, {'A-Token': 'xxx'}); $rootScope = $injector.get('$rootScope'); var $controller = $injector.get('$controller'); createController = function() { return $controller('MyController', {'$scope' : $rootScope }); }; })); afterEach(function() { $httpBackend.verifyNoOutstandingExpectation(); $httpBackend.verifyNoOutstandingRequest(); }); it('should fetch authentication token', function() { $httpBackend.expectGET('/auth'); var controller = createController(); $httpBackend.flush(); }); it('should send msg to server', function() { var controller = createController(); $httpBackend.flush(); // pour cet URL, l'authentification n'est pas importante // Le contrôleur enverra tout de même la requête // $httpBackend répondra sans devoir définir un expect // et la réponse pour cette requête $httpBackend.expectPOST('/add-msg', 'message content').respond(201, ''); $rootScope.saveMessage('message content'); expect($rootScope.status).toBe('Saving...'); $httpBackend.flush(); expect($rootScope.status).toBe(''); }); }); |
Conclusion
Grâce à $httpBackend.when, il est possible de simuler un API REST, il n’est donc pas nécessaire de mettre en place un vrai serveur REST.
Avec la méthode $httpBackend.expect, on peut vérifier si nos appels à cet API sont bien envoyés.
Il est maintenant temps de mettre en pratique ces nouvelles notions afin d’améliorer vos tests unitaires.
Références
- https://docs.angularjs.org/api/ngMock/service/$httpBackend
- https://docs.angularjs.org/api/ng/service/$http
Laisser un commentaire
Participez-vous à la discussion?N'hésitez pas à contribuer!