为什么 angular-google-maps 提供者的这个测试失败了?

Why does this test for the angular-google-maps provider fail?

我正在尝试测试使用 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 存储库提交拉取请求,则可获得加分。

您无法使用 inject 获取提供程序实例,请改用 module

这里有两个陷阱,它们都与 angular-google-maps 无关。

第一个陷阱在于服务和提供者之间的区别。文档 states 指出服务、工厂、值和常量是提供者的特例。对于像我这样的相对初学者来说,这似乎表明可以在任何地方以相同的方式对提供者和服务进行依赖注入。然而,事实恰恰相反:在任何可以注入依赖项的地方,您可以注入提供者或服务,但不能同时注入两者。

这种划分的原因在于配置时间和 运行 时间之间的严格分离(参见 module documentation). Providers are available during configuration time while services are available during run time. Run time starts after configuration time ends. .config and .provider blocks are executed at configuration time while most other types of blocks get executed at run time. The relation between provider and service definitions is illustrated in the following snippet of code, adapted from the provider documentation:

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)并且可能依赖于其他提供者。根据内部块 (function MyService) 的定义,可以在 运行 时间使用提供者的 .$get 方法从提供者中提取服务,并且可能依赖于其他服务。提供者不能是服务的注入依赖项,反之亦然,但您可以像上面那样将服务定义嵌套在提供者定义中,使其间接依赖于提供者。当您使用 angular.module(...).service 块定义“独立”服务时,Angular 会在您的背后执行类似上述代码的操作。

另一个陷阱是 angular.mock.inject,也就是我问题中单元测试的 inject,只能进行 运行 时间注入。对于配置时间注入,你必须通过创建一个具有配置时间依赖性的新模块来做“真实的事情”,即非模拟注入。这就是 hinted at. André Eife published a short tutorial on how to do this, which I found through a link at the bottom of an 我的另一个问题。

总而言之,这是解决我的问题的代码:

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

夹具 (beforeEach) 中的 'testAssist' 模块存在的唯一目的是对 uiGmapGoogleMapApiProvider 具有配置时间依赖性,因此我可以在本地 gmapProvider变量。随后对 moduleinject 的调用是簿记技巧,以确保 'testAssist'config 块得到执行。由于捕获,无需在测试用例 (it) 中进行注入,我只需验证提供者是否具有 configure 方法。请注意,第一个 angular.module 调用是常规模块定义,而第二个 module 调用是来自模拟框架 (angular.mock) 的特殊构造。

我已将上述解决方案推送到 GitHub 上的 fix1 branch