Javascript 在单元测试 AngularJS 服务时注入依赖服务

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14238490/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 16:08:01  来源:igfitidea点击:

Injecting dependent services when unit testing AngularJS services

javascriptunit-testingangularjsjasminekarma-runner

提问by Roy Truelove

I'm testing service A, but service A depends on service B (i.e. service B is injected into service A).

我正在测试服务 A,但服务 A 依赖于服务 B(即服务 B 被注入到服务 A 中)。

I've seen this questionbut my case is a bit different because in my opinion it makes more sense to mockservice B instead of injecting an actual instance of service B. I'd mock it with a jasmine spy.

我见过这个问题,但我的情况有点不同,因为在我看来,模拟服务 B 而不是注入服务 B 的实际实例更有意义。我会用茉莉花间谍来模拟它。

Here's a sample test:

这是一个示例测试:

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

The error I get is:

我得到的错误是:

Error: Unknown provider: serviceBProvider

错误:未知提供者:serviceBProvider

How could I do something like this?

我怎么能做这样的事情?

采纳答案by Valentyn Shybanov

Actually in AngularJS Dependency Injection uses the 'last wins' rule. So you can define your service in your test just after including your module and dependencies, and then when service A that you're testing will request service B using DI, AngularJS will give mocked version of service B.

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

This is often is done by defining new module like MyAppMocks, putting mocked services/values there and then just adding this module as dependency.

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

Kind of (schematically):

种类(示意图):

beforeEach(function() {
  angular.module('MyAppMocks',[]).service('B', ...));
  angular.module('Test',['MyApp','MyAppMocks']);
  ...

回答by jab

I was doing this in CoffeeScript and found an extra gotcha. (Also, I found the code on this page to be confusingly terse.) Here's a complete working example:

我在 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()

Without explicitly returning null after $provide.value, I kept getting Error: Argument 'fn' is not a function, got Object. I found the answer in this Google Groups thread.

之后没有显式返回 null $provide.value,我一直得到Error: Argument 'fn' is not a function, got Object. 我在这个Google Groups thread 中找到了答案。

回答by Richard Keller

The Valentyn solution worked for me, but there is another alternative.

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

beforeEach(function () {

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

Then when AngularJS service A request the Service B by Dependency Injection, your mock of Service B will be provided instead of the Service B from moduleThatContainsServiceA.

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

This way you don't need to create an additional angular module just to mock a Service.

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

回答by gm2008

I find the simplest method is just to inject service B and mock it. e.g. Service car depends on service Engine. Now we need to mock Engine when testing Car:

我发现最简单的方法就是注入服务 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');
  }));
});

Reference: https://github.com/angular/angular.js/issues/1635

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

回答by Nick

This is what worked for me. The key is defining a real module to be mocked. Calling angular.mock.module makes the real module mockable and allows things to be connected.

这对我有用。关键是定义一个要模拟的真实模块。调用 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
        )
    )