Injecter une simulation dans un service AngularJS

J'ai un service AngularJS écrit et j'aimerais le tester en unité.

angular.module('myServiceProvider', ['fooServiceProvider', 'barServiceProvider']). factory('myService', function ($http, fooService, barService) { this.something = function() { // Do something with the injected services }; return this; }); 

Mon fichier app.js est enregistré:

 angular .module('myApp', ['fooServiceProvider','barServiceProvider','myServiceProvider'] ) 

Je peux tester que le DI fonctionne comme tel:

 describe("Using the DI framework", function() { beforeEach(module('fooServiceProvider')); beforeEach(module('barServiceProvider')); beforeEach(module('myServiceProvder')); var service; beforeEach(inject(function(fooService, barService, myService) { service=myService; })); it("can be instantiated", function() { expect(service).not.toBeNull(); }); }); 

Cela a prouvé que le service peut être créé par le cadre DI, mais je souhaite ensuite tester le service, ce qui signifie se moquer des objets injectés.

Comment puis-je faire cela?

J'ai essayé de mettre mes objets simulés dans le module, par exemple

 beforeEach(module(mockNavigationService)); 

Et réécrire la définition du service comme suit:

 function MyService(http, fooService, barService) { this.somthing = function() { // Do something with the injected services }; }); angular.module('myServiceProvider', ['fooServiceProvider', 'barServiceProvider']). factory('myService', function ($http, fooService, barService) { return new MyService($http, fooService, barService); }) 

Mais ce dernier semble arrêter le service créé par le DI comme tout.

Est-ce que quelqu'un sait comment je peux simuler les services injectés pour mes tests unitaires?

Merci

David

    Vous pouvez vous moquer de votre service en utilisant $provide .

    Si vous avez le service suivant avec une dépendance qui a une méthode appelée getSomething:

     angular.module('myModule', []) .factory('myService', function (myDependency) { return { useDependency: function () { return myDependency.getSomething(); } }; }); 

    Vous pouvez injecter une version simulée de myDependency comme suit:

     describe('Service: myService', function () { var mockDependency; beforeEach(module('myModule')); beforeEach(function () { mockDependency = { getSomething: function () { return 'mockReturnValue'; } }; module(function ($provide) { $provide.value('myDependency', mockDependency); }); }); it('should return value from mock dependency', inject(function (myService) { expect(myService.useDependency()).toBe('mockReturnValue'); })); }); 

    Notez qu'en raison de l'appel à $provide.value vous n'avez pas besoin d'injecter explicitement myDependency n'importe où. Cela se produit sous le capot lors de l'injection de myService. Lors de la configuration de MockDependency ici, il pourrait tout aussi facilement être un espion.

    Merci à loyalBrown pour le lien vers cette superbe vidéo .

    Comme je le regarde, il n'est pas nécessaire de se moquer des services eux-mêmes. Il suffit de simuler les fonctions du service. De cette façon, vous pouvez avoir des injecteurs angulaires dans vos services réels tout au long de l'application. Ensuite, scrutez les fonctions du service au besoin en utilisant la fonction spyOn de Jasmine.

    Maintenant, si le service lui-même est une fonction, et non un objet avec lequel vous pouvez utiliser l' spyOn , il y a une autre façon de le faire. J'avais besoin de le faire, et j'ai trouvé quelque chose qui fonctionne très bien pour moi. Voyez comment vous moquez-vous du service Angulaire qui est une fonction?

    Si votre contrôleur est écrit pour prendre une dépendance comme celle-ci:

     app.controller("SomeController", ["$scope", "someDependency", function ($scope, someDependency) { someDependency.someFunction(); }]); 

    Alors vous pouvez faire une fausse someDependency dans un test Jasmine comme ceci:

     describe("Some Controller", function () { beforeEach(module("app")); it("should call someMethod on someDependency", inject(function ($rootScope, $controller) { // make a fake SomeDependency object var someDependency = { someFunction: function () { } }; spyOn(someDependency, "someFunction"); // this instantiates SomeController, using the passed in object to resolve dependencies controller("SomeController", { $scope: scope, someDependency: someDependency }); expect(someDependency.someFunction).toHaveBeenCalled(); })); }); 

    Une autre option pour faciliter les dépendances moqueuses dans Angular et Jasmine est d'utiliser QuickMock. Il peut être trouvé sur GitHub et vous permet de créer des simulations simples de manière réutilisable. Vous pouvez le cloner de GitHub via le lien ci-dessous. Le README est très explicatif, mais j'espère qu'il pourrait aider les autres dans le futur.

    https://github.com/tennisgent/QuickMock

     describe('NotificationService', function () { var notificationService; beforeEach(function(){ notificationService = QuickMock({ providerName: 'NotificationService', // the provider we wish to test moduleName: 'QuickMockDemo', // the module that contains our provider mockModules: ['QuickMockDemoMocks'] // module(s) that contains mocks for our provider's dependencies }); }); .... 

    Il gère automatiquement tous les éléments mentionnés ci-dessus, donc vous n'avez pas à écrire tout ce code d'injection simulée dans chaque test. J'espère que cela pourra aider.

    En plus de la réponse de John Galambos : si vous voulez simplement se moquer de méthodes spécifiques d'un service, vous pouvez le faire comme ceci:

     describe('Service: myService', function () { var mockDependency; beforeEach(module('myModule')); beforeEach(module(function ($provide, myDependencyProvider) { // Get an instance of the real service, then modify specific functions mockDependency = myDependencyProvider.$get(); mockDependency.getSomething = function() { return 'mockReturnValue'; }; $provide.value('myDependency', mockDependency); }); it('should return value from mock dependency', inject(function (myService) { expect(myService.useDependency()).toBe('mockReturnValue'); })); }); 

    J'ai récemment publié ngImprovedTesting qui devrait rendre les tests simulés dans la manière AngularJS plus facile.

    Pour tester 'myService' (à partir du module "myApp") avec ses dépendances fooService et barService, vous pouvez simplifier le suivi dans votre test Jasmine:

     beforeEach(ModuleBuilder .forModule('myApp') .serviceWithMocksFor('myService', 'fooService', 'barService') .build()); 

    Pour plus d'informations sur ngImprovedTesting, consultez sa publication de blog d'introduction: http://blog.jdriven.com/2014/07/ng-improved-testing-mock-testing-for-angularjs-made-easy/

    Je sais que c'est une vieille question, mais il y a une autre manière plus facile, vous pouvez créer une simulation et désactiver l'original injecté à une seule fonction, il peut être fait en utilisant spyOn sur toutes les méthodes. Voir le code ci-dessous.

     var mockInjectedProvider; beforeEach(function () { module('myModule'); }); beforeEach(inject(function (_injected_) { mockInjectedProvider = mock(_injected_); }); beforeEach(inject(function (_base_) { baseProvider = _base_; })); it("injectedProvider should be mocked", function () { mockInjectedProvider.myFunc.andReturn('testvalue'); var resultFromMockedProvider = baseProvider.executeMyFuncFromInjected(); expect(resultFromMockedProvider).toEqual('testvalue'); }); //mock all service methods function mock(angularServiceToMock) { for (var i = 0; i < Object.getOwnPropertyNames(angularServiceToMock).length; i++) { spyOn(angularServiceToMock,Object.getOwnPropertyNames(angularServiceToMock)[i]); } return angularServiceToMock; }