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

Je me moque d’un service pour un test d’unité AngularJS. J’utilise le service $provide provision pour remplacer le service “réel” par le service simulé (un script plunker 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 une fonction angular.mock.module simplement pour donner une référence au service $provide angular.mock.inject qui est ensuite utilisé dans la fonction angular.mock.inject ci-dessous. Mais si j’ajoute directement $provide comme paramètre à la fonction angular.mock.inject , j’obtiens une erreur “fournisseur inconnu”.

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

En d’autres termes, si j’ajoute un paramètre $q à la fonction angular.mock.module , angular.mock.module également une erreur “fournisseur inconnu”.

Y a-t-il un moyen de simplifier cela? De toute évidence, ce que j’ai fonctionne, mais ça ne semble pas correct, en quelque sorte. Je pense que je ne comprends pas pourquoi certains fournisseurs sont disponibles dans les fonctions d’ inject et d’autres sont disponibles dans module fonctions de module .

Vous ne pouvez pas utiliser $provide dans la fonction inject car les anciens enregistrent les fournisseurs pour que ce dernier les 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 écrire 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.

Cela a fonctionné pour moi quand je devais emballer un service qui utilisait $q et semblait 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 .