52

我正在测试服务 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();
  });
});

我得到的错误是:

错误:未知提供者:serviceBProvider

我怎么能做这样的事情?

4

5 回答 5

46

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

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

种类(示意图):

beforeEach(function() {
  angular.module('MyAppMocks',[]).service('B', ...));
  angular.module('Test',['MyApp','MyAppMocks']);
  ...
于 2013-01-09T16:33:42.200 回答
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()

在没有明确返回 null 之后$provide.value,我不断得到Error: Argument 'fn' is not a function, got Object. 我在这个Google Groups 线程中找到了答案。

于 2013-06-13T21:53:48.667 回答
20

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

beforeEach(function () {

    angular.mock.module("moduleThatContainsServiceA", function ($provide) {
                $provide.value('B', ...);
            });
});

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

这样你就不需要创建一个额外的角度模块来模拟一个服务。

于 2013-05-20T18:48:22.380 回答
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');
  }));
});

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

于 2014-07-27T10:37:21.640 回答
1

这对我有用。关键是定义要模拟的真实模块。调用 angular.mock.module 使真正的模块可模拟并允许连接事物。

    beforeEach( ->
        @weather_service_url = '/weather_service_url'
        @weather_provider_url = '/weather_provider_url'
        @weather_provider_image = "test.jpeg"
        @http_ret = 'http_works'
        module = angular.module('mockModule',[])
        module.value('weather_service_url', @weather_service_url)
        module.value('weather_provider_url', @weather_provider_url)
        module.value('weather_provider_image', @weather_provider_image)
        module.service('weather_bug_service', services.WeatherBugService)

        angular.mock.module('mockModule')

        inject( ($httpBackend,weather_bug_service) =>
            @$httpBackend = $httpBackend
            @$httpBackend.when('GET', @weather_service_url).respond(@http_ret)
            @subject = weather_bug_service
        )
    )
于 2013-08-12T13:57:49.477 回答