将模拟注入到AngularJS服务中

我有一个AngularJS服务,我想unit testing它。

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

我的app.js文件有这些注册:

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

我可以testingDI是这样工作的:

 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(); }); }); 

这certificate了服务可以由DI框架创build,但是接下来我要unit testing服务,这意味着嘲笑注入的对象。

我怎么去做这个?

我已经尝试把我的模拟对象,例如

 beforeEach(module(mockNavigationService)); 

并将服务定义重写为:

 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); }) 

但是后者似乎停止了由DI创build的服务。

有人知道我可以如何嘲笑我的unit testing注入的服务?

谢谢

大卫

您可以使用$provide将mock注入到您的服务中。

如果你有一个名为getSomething的方法的依赖的服务:

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

你可以注入一个模拟版本的myDependency,如下所示:

 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'); })); }); 

请注意,由于对$provide.value的调用,实际上并不需要在任何地方显式注入myDependency。 在我的服务注入期间发生在引擎盖下。 在这里设置mockDependency时,它可能很容易成为间谍。

感谢loyalBrown链接到伟大的video 。

从我看来,没有必要自己去嘲笑服务。 只是模拟服务的function。 这样,您可以在整个应用程序中为您的实际服务注入angular色。 然后,根据需要使用Jasmine的spyOn函数来模拟服务上的function。

现在,如果服务本身是一个函数,而不是一个可以使用spyOn的对象,还有另一种方法可以去做。 我需要这样做,并find一些对我来说很好的东西。 请参阅如何模拟angular色服务,这是一个function?

在Angular和Jasmine中,另一个可以帮助简化模拟依赖关系的方法是使用QuickMock。 它可以在GitHub上find,并允许您以可重用的方式创build简单的模拟。 你可以通过下面的链接从GitHub克隆它。 README很自我解释,但希望将来可以帮助其他人。

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 }); }); .... 

它会自动pipe理上面提到的所有样板,所以你不必在每个testing中都写出所有的模拟注入代码。 希望有所帮助。

除了John Galambos的回答 :如果你只是想嘲笑服务的具体方法,你可以这样做:

 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'); })); }); 

如果你的控制器被写入来获得这样的依赖:

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

那么你可以在这样一个茉莉花testing中做一个假的someDependency

 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(); })); }); 

我最近发布ngImprovedTesting,应该使在AngularJS方式的模拟testing更容易。

要testing“myService”(来自“myApp”模块)的fooService和barService依赖关系,你可以简单地在Jasminetesting中执行以下操作:

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

有关ngImprovedTesting的更多信息,请查看其介绍性博客文章: http ://blog.jdriven.com/2014/07/ng-improved-testing-mock-testing-for-angularjs-made-easy/

我知道这是一个老问题,但有另一种更简单的方法,你可以创build模拟和禁用原始注入在一个function,它可以通过使用spyOn所有的方法。 看下面的代码。

 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; }