在单元测试AngularJS服务时注入相关服务

Roy*_*ove 51 javascript unit-testing jasmine angularjs karma-runner

我正在测试服务A,但服务A依赖于服务B(即服务B注入服务A).

我已经看到了这个问题,但我的情况有点不同,因为在我看来,模拟服务B而不是注入服务B的实际实例更有意义.我用茉莉花间谍嘲笑它.

这是一个示例测试:

describe("Sample Test Suite", function() {

  beforeEach(function() {

    module('moduleThatContainsServiceA');

    inject([
      'serviceA', function(service) {
        this.service = service;
      }
    ]);

  });

  it('can create an instance of the service', function() {
    expect(this.service).toBeDefined();
  });
});
Run Code Online (Sandbox Code Playgroud)

我得到的错误是:

错误:未知提供者:serviceBProvider

我怎么能这样做?

Val*_*nov 45

实际上在AngularJS中,依赖注入使用"最后获胜"规则.因此,您可以在包含模块和依赖项之后立即在测试中定义服务,然后当您正在测试的服务A将使用DI请求服务B时,​​AngularJS将提供服务B的模拟版本.

这通常是通过定义像MyAppMocks这样的新模块,将模拟服务/值放在那里然后只是将此模块添加为依赖项来完成的.

种类(示意图):

beforeEach(function() {
  angular.module('MyAppMocks',[]).service('B', ...));
  angular.module('Test',['MyApp','MyAppMocks']);
  ...
Run Code Online (Sandbox Code Playgroud)

  • 托马斯,你能分享一下你的解决方案的一些细节吗 我有2个模块,每个模块都包含一个服务,第一个模块的service_1在第二个模块的service_2中注入.我正在创建一个带有service_1的模拟模块,该模块应该覆盖原始service_1.并且它被覆盖,但仅在测试中,所以当我调用service_2时,它仍在使用原始service_1. (3认同)
  • 我如何处理[为服务添加模拟](https://github.com/daniellmb/angular-test-patterns/blob/master/patterns/service.md#suggested-service-unit-test-setup-) (3认同)
  • 你救了我的命!:DI正在杀死我自己试图将一个模拟服务注入另一个依赖它的服务,但只有测试会使用模拟版本,而不是注入服务.现在我有一个单独的模拟模块,我在app模块之后加载,覆盖了所需的服务.奇迹般有效! (2认同)

jab*_*jab 24

我在CoffeeScript中这样做,发现了额外的问题.(另外,我发现这个页面上的代码容易混淆.)这是一个完整的工作示例:

describe 'serviceA', ->
   mockServiceB = {}

   beforeEach module 'myApp' # (or just 'myApp.services')

   beforeEach ->
      angular.mock.module ($provide) ->
         $provide.value 'serviceB', mockServiceB
         null

   serviceA = null
   beforeEach inject ($injector) ->
      serviceA = $injector.get 'serviceA'

   it 'should work', ->
      expect( true ).toBe( true )
      #serviceA.doStuff()
Run Code Online (Sandbox Code Playgroud)

如果没有显式返回null $provide.value,我就会继续Error: Argument 'fn' is not a function, got Object.我在此Google网上论坛帖子中找到了答案.


小智 20

Valentyn解决方案对我有用,但还有另一种选择.

beforeEach(function () {

    angular.mock.module("moduleThatContainsServiceA", function ($provide) {
                $provide.value('B', ...);
            });
});
Run Code Online (Sandbox Code Playgroud)

然后,当AngularJS服务A通过依赖注入请求服务B时,​​将从moduleThatContainsServiceA提供服务B的模拟而不是服务B.

这样,您无需为了模拟服务而创建额外的角度模块.


gm2*_*008 6

我发现最简单的方法就是注入服务B并模拟它.例如服务车取决于服务引擎.现在我们需要在测试Car时模拟Engine:

describe('Testing a car', function() {
      var testEngine;

  beforeEach(module('plunker'));
  beforeEach(inject(function(engine){
    testEngine = engine;
  }));

  it('should drive slow with a slow engine', inject(function(car) {
    spyOn(testEngine, 'speed').andReturn('slow');
    expect(car.drive()).toEqual('Driving: slow');
  }));
});
Run Code Online (Sandbox Code Playgroud)

参考:https://github.com/angular/angular.js/issues/1635