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

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

Я видел этот вопрос , но мое дело немного другое, потому что, на мой взгляд, имеет смысл mock сервис 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

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

50
добавлено отредактировано
Просмотры: 2
FWIW: Я задал версию QUnit этого вопроса здесь, на CodeReview.SE ,
добавлено автор Jeroen, источник

5 ответы

Фактически в AngularJS Dependency Injection используется правило «последних побед». Таким образом, вы можете определить свою услугу в своем тесте сразу после включения вашего модуля и зависимостей, а затем, когда служба A, которую вы тестируете, будет запрашивать службу B с помощью DI, AngularJS предоставит издеваемую версию службы B.

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

Вид (схематично):

beforeEach(function() {
  angular.module('MyAppMocks',[]).service('B', ...));
  angular.module('Test',['MyApp','MyAppMocks']);
  ...
43
добавлено
@clearf в разделе «...» вы определяете макет службы «B», который обеспечивает необходимую функциональность для тестируемого контроллера. Таким образом, в основном, вы реализуете только часть обслуживания B, создавая шпионы (например) и удостоверяясь, что в конце теста были вызваны все необходимые методы обслуживания «B».
добавлено автор Valentyn Shybanov, источник
Как я обрабатываю добавление mocks в сервисы
добавлено автор daniellmb, источник
Ты просто спас мне жизнь! : D Я убивал себя, пытаясь ввести издеваемую службу в другую службу, которая зависела от нее, но только тесты использовали бы издеваемую версию, а не инъецированную службу. Теперь у меня есть отдельный модуль макета, который загружается после модуля приложения, который перезаписывает нужные службы. Работает как шарм!
добавлено автор Thomas Fankhauser, источник
Хотя я уверен, что этот ответ верен, я немного смущен «...». Я думаю об этом в контексте, где я хочу проверить контроллер в модуле, который зависит от «MyApp». Что происходит в ...? Могу ли я тестировать функциональность модуля «Тест»?
добавлено автор clearf, источник
Томас, не могли бы вы поделиться некоторыми сведениями о своем решении? У меня есть 2 модуля, каждый из которых содержит службу, а service_1 из первого модуля вводится в service_2 во втором модуле. Я создаю модуль mock с service_1, который должен перезаписать оригинальный service_1. И он перезаписывается, но только в тестах, поэтому, когда я вызываю service_2, он все еще использует оригинальный service_1 внутри.
добавлено автор Selvam Palanimalai, источник

Я делал это в 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 после $ provided.value я продолжал получать Ошибка: аргумент «fn» не является функцией, получил Object . Я нашел ответ в этом разделе Google Groups .

24
добавлено
@MathieuBrouwers, вам, вероятно, следует открыть новый вопрос. Я не уверен, что вы имеете в виду, и мне нужно будет увидеть, как ваш код отвечает.
добавлено автор jab, источник
Попробуйте использовать пустой return вместо null . Таким образом, у вашего сгенерированного JavaScript не будет дополнительной строки return null в конце функции. Вместо этого ваша функция beforeEach просто ничего не вернет.
добавлено автор demisx, источник
Я пытаюсь использовать аналогичный код на данный момент, но я получаю код SyntaxError: Unexpecte 'serviceA' , если я буду использовать ваш код. Любая идея о том, как это решить? - Также используя Coffeescript
добавлено автор Mathieu Brouwers, источник
Я нашел проблему к настоящему времени, это было, когда я попытался фактически проверить CoffeeScript вместо скомпилированного кода javascript. Думаю, мой комментарий бесполезен и может быть удален сейчас.
добавлено автор Mathieu Brouwers, источник

Решение Валентина работало для меня, но есть еще одна альтернатива.

beforeEach(function() {

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

Затем, когда услуга A AngularJS запрашивает услугу B путем впрыскивания зависимостей, ваш макет службы B будет предоставлен вместо службы B из модуляThatContainsServiceA.

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

20
добавлено
Отлично. В моем случае «...» заменили на «{}». Бам, полностью удалил зависимость.
добавлено автор 2mia, источник
Проблема в том, что вы все еще издеваетесь над своим сервисом внутри теста. Скажите, что вам нужно издеваться над этим в нескольких местах, и что-то меняется. Тогда вам придется менять каждый файл вместо того, чтобы меняться в одном месте. Для удобства обслуживания Валентин должен быть принят (что есть).
добавлено автор perry, источник

Я нахожу, что самый простой метод - это просто ввести услугу 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');
  }));
});

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

6
добавлено

Это то, что сработало для меня. Ключ определяет реальный модуль для издевательства. Вызов 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
        )
    )
1
добавлено