Инжектирование зависимых служб при модульном тестировании служб AngularJS

Я тестирую службу A, но служба A зависит от службы B (т. е. служба B внедряется в службу A).

Я видел этот вопрос, но мой случай немного отличается, потому что, на мой взгляд, имеет смысл подражать сервису B вместо того, чтобы инжектировать реальный экземпляр сервиса B. Я бы подражал ему с помощью jasmine spy.

Вот пример теста:

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

Как я могу сделать что-то подобное?

Комментарии к вопросу (1)
Решение

На самом деле в AngularJS Dependency Injection используется правило 'last wins'. Таким образом, вы можете определить свой сервис в тесте сразу после включения модуля и зависимостей, а затем, когда тестируемый вами сервис A запросит сервис B с помощью DI, AngularJS выдаст mocked-версию сервиса B.

Часто это делается путем определения нового модуля, например MyAppMocks, помещения туда подражаемых сервисов/значений, а затем просто добавления этого модуля в качестве зависимости.

Типа того (схематично):

beforeEach(function() {
  angular.module('MyAppMocks',[]).service('B', ...));
  angular.module('Test',['MyApp','MyAppMocks']);
  ...
Комментарии (6)

Я делал это на 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.

Комментарии (4)

Мне помогло решение с Валентином, но есть и другая альтернатива.

beforeEach(function () {

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

Тогда при запросе AngularJS сервиса A к сервису B через Dependency Injection, вместо сервиса B из moduleThatContainsServiceA будет предоставлен ваш макет сервиса B.

Таким образом, вам не нужно создавать дополнительный модуль Angular только для имитации сервиса.

Комментарии (2)

Я считаю самый простой способ это просто, чтобы придать обслуживание B и глумиться над ней. например, автомобиль служба зависит от службы двигателя. Теперь нам нужно глумиться двигателя при тестировании автомобиля:

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

Комментарии (0)

Это работало для меня. Ключевым является определение реального модуля на поругание. Звоню угловой.глумитесь.модуль делает из модуля и позволяет вещам быть подключен.

    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
        )
    )
Комментарии (0)