web-dev-qa-db-fra.com

Test des services $ resource dans AngularJS

J'essaie de commencer à écrire des tests unitaires pour mon angular et j'ai frappé un bloc d'arrêt assez rapidement car je ne sais pas exactement comment se moquer de mon service d'une manière testable.
Existe-t-il un moyen de se moquer de l'appel REST sinon 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 Je suis plutôt nouveau pour tester l'écriture alors peut-être est-ce ainsi qu'il est censé être accompli.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'});
      }));
   })
});
37
Brian

Vous pouvez vous moquer des demandes 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');
        }));

    });
});

Demo

54
zs2020

réponse de zsong m'a beaucoup aidé à comprendre cela, mais je voudrais développer son fonctionnement. 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');
        }));

    });
});

Que se passe t-il ici?

1

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

Nous disons à l'injecteur Angular ( $injector et angular.mock.inject ) d'injecter les choses définies dans le module myApp. Vous pouvez le considérer comme définissant une dépendance de module sans module dépendant. Comparez avec la façon dont les choses définies dans le module myApp peuvent être injectées dans, disons, un contrôleur dans un angular.module('myOtherApp', ['myApp']).

2

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

Avant chaque spécification, exécutez la fonction function ($injector) avec les dépendances injectées. Dans ce cas, la dépendance ($injector) est résolue 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 plutôt déclaré explicitement les dépendances et 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 le service User implicitement résolu comme paramètre, bien qu'il ne soit pas réellement utilisé.

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

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