Почему этот тест для провайдера angular-google-maps не работает?

Я пытаюсь протестировать модуль, использующий angular-google-maps. Ошибка, потому что angular.mock.inject не может найти uiGmapGoogleMapApiProvider:

Error: [$injector:unpr] Unknown provider: uiGmapGoogleMapApiProviderProvider <- uiGmapGoogleMapApiProvider

Я не могу понять, что происходит не так. Вот сокращенный тестовый пример:

'use strict';

describe('this spec', function() {
    beforeEach(module('uiGmapgoogle-maps'));

    it('tries to configure uiGmapGoogleMapApiProvider', inject(function(uiGmapGoogleMapApiProvider) {
        expect(uiGmapGoogleMapApiProvider.configure).toBeDefined();
    }));
});

Все это доступно в виде готового к запуску проекта Angular на GitHub. Если вы обнаружите проблему, пожалуйста, ответьте здесь, на Stack Overflow. Бонусные баллы, если вы также отправляете запрос на включение в репозиторий GitHub.


person Julian    schedule 11.08.2016    source источник


Ответы (2)


Здесь взаимодействуют две ловушки, ни одна из которых не имеет ничего общего с angular-google-maps.

Первая ловушка заключается в различии между услугами и поставщиками. В документации указано, что сервисы, фабрики, значения и константы являются частными случаями провайдеров. Для такого относительного новичка, как я, это, кажется, предполагает, что провайдеры и сервисы могут быть внедрены зависимостями где угодно одним и тем же способом. Однако верно и обратное: в любом месте, где могут быть внедрены зависимости, вы можете внедрить либо провайдеров, либо сервисы, но никогда и то, и другое.

Причина такого разделения заключается в строгом разделении времени настройки и времени выполнения (см. документацию модуля). Поставщики доступны во время настройки, а службы доступны во время выполнения. Время выполнения начинается после окончания времени настройки. Блоки .config и .provider выполняются во время настройки, в то время как большинство других типов блоков выполняются во время выполнения. Связь между определениями провайдера и службы проиллюстрирована в следующем фрагменте кода, адаптированном из провайдера. документация:

myModule.provider('myServiceProvider', ['injectedProvider', function MyServiceProvider(injectedProvider) {
    // configuration time code depending on injectedProvider

    this.$get = ["injectedService", function MyService(injectedService) {
        // run time code depending on injectedService
    }];

    // more configuration time code
}]);

Как видите, служба определяется внутри поставщика. Поставщик определяется во время настройки (внешний блок, function MyServiceProvider) и может зависеть от других поставщиков. Служба может быть извлечена из провайдера с помощью метода .$get провайдера во время выполнения, как определено внутренним блоком (function MyService), и может зависеть от других служб. Поставщик не может быть внедренной зависимостью службы или наоборот, но вы можете вложить определение службы в определение поставщика, как показано выше, чтобы он зависел от поставщиков косвенно. Когда вы определяете автономный сервис с помощью блока angular.module(...).service, Angular за вашей спиной делает что-то вроде приведенного выше кода.

Другая ловушка заключается в том, что angular.mock.inject, который является inject из модульного теста в моем вопросе, может выполнять инъекции только во время выполнения. Для инъекций времени конфигурации вы должны делать реальную вещь, то есть инъекцию без имитации, создавая новый модуль с зависимостями времени конфигурации. Это то, на что намекал mguimard. Андре Эйфе опубликовал короткое руководство по как это сделать, которую я нашел по ссылке внизу ответа на мой другой вопрос.

В заключение, вот код, который решит проблему в моем вопросе:

'use strict';

describe('this spec', function() {
    var gmapProvider;

    beforeEach(function() {
        angular.module('testAssist', ['uiGmapgoogle-maps'])
        .config(function(uiGmapGoogleMapApiProvider) {
            gmapProvider = uiGmapGoogleMapApiProvider;
        });
        module('testAssist');  // angular.mock.module
        inject();              // angular.mock.inject
    });

    it('tries to configure uiGmapGoogleMapApiProvider', function() {
        expect(gmapProvider.configure).toBeDefined();
    });
});

Модуль 'testAssist' в приспособлении (beforeEach) существует с единственной целью — иметь зависимость времени конфигурации от uiGmapGoogleMapApiProvider, поэтому я могу зафиксировать последний в локальной переменной gmapProvider. Последующие вызовы module и inject являются бухгалтерскими уловками, чтобы гарантировать выполнение блока config 'testAssist'. Благодаря захвату в тестовом примере (it) не нужно делать инъекции, и я могу просто убедиться, что у провайдера есть метод configure. Обратите внимание, что первый вызов angular.module — это определение обычного модуля, а второй вызов module — это специальная конструкция из фиктивной среды (angular.mock).

Я отправил вышеуказанное решение в ветку fix1. на GitHub.

person Julian    schedule 17.08.2016

Вы не можете получить экземпляр провайдера, используя inject, вместо этого используйте module

person mguimard    schedule 11.08.2016
comment
Похоже, это не согласуется с примерами кода в другом месте. Не могли бы вы показать код, иллюстрирующий ваш ответ? - person Julian; 11.08.2016
comment
После дополнительных собственных исследований, я думаю, я понимаю, что вы имеете в виду. Однако другим людям не обязательно знать то, что знаете вы, чтобы понять ваш ответ. Пожалуйста, объясните, что происходит, и приведите пример кода, иначе это не стоит того, чтобы голосовать. - person Julian; 17.08.2016