向 AngularJS 服务注入模拟

我有一个 AngularJS 服务编写,我想单元测试它。

angular.module('myServiceProvider', ['fooServiceProvider', 'barServiceProvider']).
factory('myService', function ($http, fooService, barService) {


this.something = function() {
// Do something with the injected services
};


return this;
});

My app.js file has these registered:

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

and rewriting the service definition as:

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 创建的服务。

有人知道我如何在单元测试中模仿注入的服务吗?

谢谢

大卫

98485 次浏览

在我看来,没有必要嘲笑服务本身。只需模拟服务上的函数。这样,你可以有角注入您的整个应用程序的真正的服务。然后,根据需要使用 Jasmine 的 spyOn函数模拟服务上的函数。

现在,如果服务本身是一个函数,而不是一个可以使用 spyOn的对象,那么还有另外一种方法。我需要这么做,而且找到了对我很有用的东西。见 你如何嘲笑角服务是一个函数?

可以使用 $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。在注入 myService 时,它发生在引擎盖下。当在这里设置 mock Dependency 时,它也可以很容易地成为一个间谍。

感谢 忠诚的布朗连接到 那个很棒的视频

If your controller is written to take in a dependency like this:

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

然后你可以在茉莉花试验中做一个假的 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();
}));
});

我最近发布了 ngOptimedTest,它可以使 AngularJS 中的模拟测试更加容易。

要测试“ myService”(来自“ myApp”模块) ,使用它的 fooService 和 barService 依赖项,可以在 Jasmine 测试中简单地执行以下操作:

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

想要了解更多关于 ngShamedTest 的信息,请查看它的介绍性博客文章: http://blog.jdriven.com/2014/07/ng-improved-testing-mock-testing-for-angularjs-made-easy/

另一个帮助在 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
});
});
....

它自动管理上面提到的所有样板,所以您不必在每个测试中写出所有的模拟注入代码。希望能帮上忙。

我知道这是一个老问题,但还有另一个更简单的方法,你可以创建模拟和禁用原始注入的一个函数,它可以通过使用 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;
}

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


});