AngularJS覆盖(模拟)服务进行单元测试(AngularJS override (mock) services for unit test)

该场景非常简单,我试图测试服务A,这取决于服务B,因此我决定在注入服务A之前通过覆盖它来模拟服务B.但是,我也测试服务B不再有效因为它被覆盖。 任何想法如何避免这种情况?

应用代码。

angular.module('MyApp.services') .factory('serviceB', function(){ return { greeting: function(){ return 'hello'; } } }) .factory('serviceA', ['serviceB', function(serviceB){ return { greeting: function(){ return 'I say: ' + serviceB.greeting(); } });

单元测试:

describe('My app services', function(){ beforeEach(module('MyApp.services')); describe('service A', function(){ // mock service B angular.module('MyApp.services').factory('serviceB', function(){ return { greeting: function(){ return 'mocked B'; } } }); var serviceA; inject(function(_serviceA_){ serviceA = _serviceA_; }); it('should work', function(){ var words = serviceA.greeting(); expect(words).toBe('I say: mocked B'); }); }); describe('service B'. function(){ var serviceB; inject(function(_serviceB_){ serviceB = _serviceB_; }); it('should work', function(){ var words = serviceB.greeting(); expect(words).toBe('hello'); }); }); });

The scenario is very simple, I am trying to test a service A, which depends on service B, so I decided to mock service B by overriding it before I inject service A. However, I am also testing service B which is no longer valid since it is overridden. Any idea on how to avoid this?

Application code.

angular.module('MyApp.services') .factory('serviceB', function(){ return { greeting: function(){ return 'hello'; } } }) .factory('serviceA', ['serviceB', function(serviceB){ return { greeting: function(){ return 'I say: ' + serviceB.greeting(); } });

Unit Test:

describe('My app services', function(){ beforeEach(module('MyApp.services')); describe('service A', function(){ // mock service B angular.module('MyApp.services').factory('serviceB', function(){ return { greeting: function(){ return 'mocked B'; } } }); var serviceA; inject(function(_serviceA_){ serviceA = _serviceA_; }); it('should work', function(){ var words = serviceA.greeting(); expect(words).toBe('I say: mocked B'); }); }); describe('service B'. function(){ var serviceB; inject(function(_serviceB_){ serviceB = _serviceB_; }); it('should work', function(){ var words = serviceB.greeting(); expect(words).toBe('hello'); }); }); });

最满意答案

您的服务注射应该可能在beforeEach或作为测试的一部分,否则我认为它们会在测试开始之前发生,或者根本不会发生,例如

beforeEach(inject(function(_serviceA_){ serviceA = _serviceA_; }));

要么

it('should work', inject(function(serviceA){ var words = serviceA.greeting(); expect(words).toBe('I say: mocked B'); }));

要解决你实际问的问题,我建议你做两件事之一:

覆盖注入器而不是模块中的服务

beforeEach(module(function($provide){ $provide.value('serviceB', { greeting: function(){ return 'mocked B'; } }); }));

参见plunker 。

使用可在需要时添加的单独模块来覆盖serviceB

某处(只需要调用一次),将新模块的定义包含您的模拟版本的serviceB

angular.module('MyApp.services.mock', []) .factory('serviceB', function(){ return { greeting: function(){ return 'mocked B'; } } });

然后,您当前在测试模块中更改服务的位置,使用以下内容将模拟加载到顶部。

beforeEach(module('MyApp.services.mock'));

参见plunker

Your service injection should probably be either in a beforeEach or as part of the tests, otherwise I think they will happen before the tests begin or not at all, e.g.

beforeEach(inject(function(_serviceA_){ serviceA = _serviceA_; }));

or

it('should work', inject(function(serviceA){ var words = serviceA.greeting(); expect(words).toBe('I say: mocked B'); }));

To solve the problem you are actually asking about, I suggest you do one of two things:

Override the service in the injector rather than the module

beforeEach(module(function($provide){ $provide.value('serviceB', { greeting: function(){ return 'mocked B'; } }); }));

See plunker.

Use a separate module that can be added when needed to override serviceB

Somewhere (that only needs to be called once), put the definition of your new module containing your mock version of serviceB

angular.module('MyApp.services.mock', []) .factory('serviceB', function(){ return { greeting: function(){ return 'mocked B'; } } });

then where you currently alter the service in your module under test, use the following to load your module with the mock on top.

beforeEach(module('MyApp.services.mock'));

See plunker

更多推荐