web-dev-qa-db-fra.com

Pourquoi $ provide n'est-il disponible que dans la fonction 'angular.mock.module' et $ q est-il uniquement disponible dans la fonction 'angular.mock.inject'?

Je moque un service pour un test unitaire AngularJS. J'utilise le $provide service pour remplacer le service "réel" par le service simulé (un script plunker de ceci est disponible):

describe('My Controller', function () {

    var $scope;
    var $provide;

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

    beforeEach(angular.mock.module(function (_$provide_) {

        $provide = _$provide_;

    }));

    beforeEach(angular.mock.inject(function($rootScope, $controller, $q){

        var mockMyService = {
            getAll : function() {
                var deferred = $q.defer();
                deferred.resolve([
            { itemText: "Foo" },
            { itemText: "Bar" }
                ]);

                return deferred.promise;
            }
        };

        $provide.value('myService', mockMyService);

        $scope = $rootScope.$new();

        $controller('MyCtrl', { $scope: $scope });

        $rootScope.$apply();

    }));

    it('Has two items defined', function () {
        expect($scope.items.length).toEqual(2);
    });
});

Cela fonctionne très bien. Cependant, je n'aime pas le fait que j'utilise un angular.mock.module fonctionne simplement pour donner une référence à $provide service qui est ensuite utilisé dans le angular.mock.inject fonction ci-dessous. Mais si j'ajoute $provide comme paramètre du angular.mock.inject fonctionne directement à la place, j'obtiens une erreur "fournisseur inconnu".

Il me semble que je pourrais mettre tout le code moqueur dans le angular.mock.module une fonction. Mais j'ai un problème similaire avec le $q référence, dont j'ai besoin car mon service moqué doit retourner une promesse.

En d'autres termes, si j'ajoute un $q paramètre au angular.mock.module alors j'obtiens également une erreur "fournisseur inconnu".

Existe-t-il un moyen de simplifier cela? Évidemment, ce que j'ai fonctionne, mais ça ne me semble pas très bien, d'une manière ou d'une autre. Je sens que je ne comprends pas pourquoi certains fournisseurs sont disponibles dans les fonctions inject et d'autres sont disponibles dans les fonctions module.

39
Holf

Vous ne pouvez pas utiliser $provide dans la fonction inject car le premier enregistre les fournisseurs pour que le second l'utilise. Regarde:

describe('...', function() {
    beforeEach(function() {
        module(function($provide) {
            $provide.constant('someValue', 'foobar');
        });

        inject(function(someValue) {
            var value = someValue; // will be 'foobar';
        });
    });
});

Vous pouvez cependant rédiger votre test de cette façon:

describe('...', function() {
    var serviceMock;

    beforeEach(function() {
        serviceMock = {
           someMethod: function() { ... }
        };

        module(function($provide) {
            $provide.value('service', serviceMock);
        });

        inject(function(service) {
            ...                         
        });
    });
});

En fait, vous n'avez même pas besoin d'implémenter le service simulé avant de l'injecter avec $provide:

beforeEach(function() {
    serviceMock = {};

    module(function($provide) {
        $provide.value('service', serviceMock);
    });

    inject(function(service) {
        ...                         
    });
});

it('tests something', function() {
    // Arrange
    serviceMock.someMethod = function() { ... }

    // Act
    // does something

    // Assert
    expect(...).toBe(...);
});

Voici un script Plunker illustrant principalement ce qui précède.

55
Michael Benford

Cela a fonctionné pour moi lorsque j'ai dû envelopper un service qui utilisait $q et semble assez propre:

var _ServiceToTest_;
beforeEach(function () {
    module('module.being.tested');
    module(function ($provide) {
        $provide.factory('ServiceToMock', function ($q, $rootScope) {
            var service = ...;
            // use $q et al to heart's content
            return service;
        });
    });
    inject(function (_ServiceToTest_) {
        ServiceToTest = _ServiceToTest_;
    });
});

it('...', function () { /* code using ServiceToTest */ });

L'astuce consistait à utiliser $provide.factory au lieu de $provide.value.

11
Claudiu