37 votes

Tester les services $resource en AngularJS

J'essaie de commencer à écrire des tests unitaires pour mon application angulaire et je me heurte à un blocage assez rapide car je ne sais pas exactement comment simuler mon service d'une manière testable.
Existe-t-il un moyen de simuler l'appel REST ? Dans le cas contraire, il semblerait que je doive refléter tout ce qui se trouve dans mon service dans mes tests, ce qui ne me semble pas correct, mais je suis plutôt novice dans l'écriture de tests, donc peut-être que c'est ainsi qu'il faut procéder. Toute aide serait grandement appréciée.

Mon service est le suivant :

angular.module('resources.users', ['ngResource'])
.factory('User', function($resource) {
   var resource = $resource('/api/index.php/users/:username', {}, {
      'update': {method: 'PUT'}
   });

   resource.getUser = function(username, successCb) {
      return resource.query({username: username}, successCb);
   };

   return resource;
});

Mon test consiste jusqu'à présent à :

describe('User', function() {
    var mockUserResource;
    beforeEach(module('resources.users'));
    beforeEach(function() {
        mockUserResource = sinon.stub({
            getUser: function(username) {
                mockUserResource.query({username: username});
            },
            query: function() {}
        });
        module(function($provide) {
            $provide.value('User', mockUserResource);
        })
   });
   describe('getUser', function() {
      it('should call getUser with username', inject(function(User) {
          User.getUser('test');
          expect(mockUserResource.query.args[0][0]).toEqual({username: 'test'});
      }));
   })
});

54voto

zsong Points 20492

Vous pouvez simuler les requêtes faites par ngResource comme ceci :

describe('User', function () {
    var mockUserResource, $httpBackend;
    beforeEach(angular.mock.module('myApp'));

    beforeEach(function () {
        angular.mock.inject(function ($injector) {
            $httpBackend = $injector.get('$httpBackend');
            mockUserResource = $injector.get('User');
        })
    });

    describe('getUser', function () {
        it('should call getUser with username', inject(function (User) {
            $httpBackend.expectGET('/api/index.php/users/test')
                .respond([{
                username: 'test'
            }]);

            var result = mockUserResource.getUser('test');

            $httpBackend.flush();

            expect(result[0].username).toEqual('test');
        }));

    });
});

<strong><a href="http://jsfiddle.net/8FRBS/">Demo</a></strong>

31voto

Emil Lundberg Points 1078

La réponse de zsong m'a grandement aidé à comprendre cela, mais j'aimerais développer la façon dont cela fonctionne. Au cas où il serait édité, je liste à nouveau le code ici :

describe('User', function () {
    var mockUserResource, $httpBackend;
    beforeEach(angular.mock.module('myApp'));

    beforeEach(function () {
        angular.mock.inject(function ($injector) {
            $httpBackend = $injector.get('$httpBackend');
            mockUserResource = $injector.get('User');
        })
    });

    describe('getUser', function () {
        it('should call getUser with username', inject(function (User) {
            $httpBackend.expectGET('/api/index.php/users/test')
                .respond([{
                username: 'test'
            }]);

            var result = mockUserResource.getUser('test');

            $httpBackend.flush();

            expect(result[0].username).toEqual('test');
        }));

    });
});

Qu'est-ce qui se passe ici ?

1

beforeEach(angular.mock.module('myApp'));

Nous disons à l'injecteur angulaire ( $injector et angular.mock.inject ) pour injecter les éléments définis dans le myApp module. Vous pouvez considérer que c'est comme définir une dépendance de module sans module dépendant. Comparez avec la façon dont les choses définies dans le myApp peut être injecté, par exemple, dans un contrôleur d'un système de gestion de l'information. angular.module('myOtherApp', ['myApp']) module.

2

beforeEach(function () {
    angular.mock.inject(function ($injector) {
        $httpBackend = $injector.get('$httpBackend');
        mockUserResource = $injector.get('User');
    })
});

Avant chaque spéculation, exécutez le function ($injector) avec des dépendances injectées. Dans ce cas, la dépendance ( $injector ) est résolu implicitement à partir du nom du paramètre. Une variante fonctionnellement équivalente de cet extrait est

beforeEach(function () {
    angular.mock.inject(['$httpBackend', 'User', function ($httpB, User) {
        $httpBackend = $httpB;
        mockUserResource = User;
    }]);
});

Ici, nous avons déclaré les dépendances de manière explicite, et nous sommes libres d'utiliser les noms de paramètres que nous souhaitons.

3

it('should call getUser with username', inject(function (User) {

Encore une fois, la fonction de test est injectée avec l'élément implicitement résolu User en tant que paramètre, bien qu'il ne soit pas réellement utilisé.

Remarquez que cette fois-ci, il n'y a pas de fonction d'encapsulation autour de la fonction inject appeler. inject invoque la fonction passée immédiatement si une spécification est en cours d'exécution, mais sinon elle renvoie une fonction d'encapsulage (voir la fonction injecter docs et code source ), nous n'avons donc pas besoin de la fonction wrapper. Ainsi, nous aurions pu écrire la fonction beforeEach l'extrait ci-dessus comme ceci :

beforeEach(angular.mock.inject(function ($injector) {
    $httpBackend = $injector.get('$httpBackend');
    mockUserResource = $injector.get('User');
}));

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X