[英]Injecting a mock into an AngularJS service
我写了一个AngularJS服务,我想对它进行单元测试。
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']
)
我可以测试DI是这样工作的:
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();
});
});
这证明了服务可以由DI框架创建,但是接下来我想对服务进行单元测试,这意味着模拟注入的对象。
我该怎么做呢?
我已经尝试将模拟对象放在模块中,例如
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创建的服务。
有人知道如何模拟我的单元测试的注入服务吗?
谢谢
大卫
您可以使用$provide
为您的服务注入模拟。
如果您具有以下服务,其依赖项具有名为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链接到那个精彩的视频 。
我看待它的方式,没有必要自己模拟服务。 只需模拟服务上的功能。 这样,您可以像在整个应用程序中一样注入角度注入真实服务。 然后,使用Jasmine的spyOn
函数根据需要模拟服务上的函数。
现在,如果服务本身是一个函数,而不是一个可以使用spyOn
的对象,那么还有另一种方法可以解决它。 我需要做到这一点,找到适合我的东西。 请参阅如何模拟作为函数的Angular服务?
在Angular和Jasmine中帮助简化模拟依赖项的另一个选项是使用QuickMock。 它可以在GitHub上找到,并允许您以可重用的方式创建简单的模拟。 您可以通过下面的链接从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
});
});
....
它自动管理上面提到的所有样板,因此您不必在每个测试中写出所有模拟注入代码。 希望有所帮助。
除了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();
}]);
然后你可以在Jasmine测试中做一个假的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中的模拟测试更容易。
要测试'myService'(来自“myApp”模块)并使用其fooService和barService依赖项进行模拟,您可以在Jasmine测试中执行以下操作:
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/
我知道这是一个老问题,但还有另一种更简单的方法,你可以创建模拟并禁用在一个函数注入的原始,它可以通过在所有方法上使用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;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.