如何测试Angular模块的配置功能?

Jul*_*ian 8 javascript angularjs karma-runner karma-jasmine angular-mock

我在configAngular 的函数中定义了一些module我想要进行单元测试的设置代码.我不清楚我应该怎么做.下面是一个简化的测试用例,展示了我是如何陷入困境的:

'use strict';

angular.module('myModule', []).config(['$http', '$log', function($http, $log) {
    $http.get('/api/getkey').then(function success(response) {
        $log.log(response.data);
    });
}]);

describe('myModule', function() {
    it('logs a key obtained from XHR', inject(function($httpBackend) {
        $httpBackend.expectGET('/api/getkey').respond(200, '12345');
        angular.module('myModule');
        $httpBackend.flush();
    }));
});
Run Code Online (Sandbox Code Playgroud)

这显然不是正确的方法,因为我收到以下错误:

Error: No pending request to flush !
Run Code Online (Sandbox Code Playgroud)

与上面的测试代码完整的,现成的运行角度的项目可以发现在GitHub上.如果您知道如何处理此场景,请在此处回答Stack Overflow.如果您还向GitHub仓库提交拉取请求,则可获得奖励积分.

mzu*_*lch 5

如果您的初始化需要注入服务,请使用run而不是config.该config函数只能接收提供者和常量作为参数,而不是像$http(相关文档)那样的实例化服务.

angular.module('myModule', []).run(['$http', '$log', function($http, $log) {
    ...
}]);
Run Code Online (Sandbox Code Playgroud)

初始化模块以进行测试

beforeEach(module('myModule'));

it('logs a key obtained from XHR', inject(function($httpBackend) {
    $httpBackend.expectGET('/api/getkey').respond(200, '12345');
    $httpBackend.flush();
}));
Run Code Online (Sandbox Code Playgroud)

所以完整的工作版本看起来像

'use strict';

angular.module('myModule', []).run(['$http', '$log', function($http, $log) {
    $http.get('/api/getkey').then(function success(response) {
        $log.log(response.data);
    });
}]);

describe('myModule', function() {
    beforeEach(module('myModule'));

    it('logs a key obtained from XHR', inject(function($httpBackend) {
        $httpBackend.expectGET('/api/getkey').respond(200, '12345');
        $httpBackend.flush();
    }));
});
Run Code Online (Sandbox Code Playgroud)

此外,这是一个测试配置块以检查提供者上的方法是否被调用的示例:https://medium.com/@a_eife/testing-config-and-run-blocks-in-angularjs-1809bd52977e#71e0


Jul*_*ian 3

mzulch 正确地指出服务不能注入到angular.module(...).config块中。他还针对模块初始化代码中实际需要使用服务的场景提供了正确的解决方案:使用块.run而不是.config块。他的回答非常适合这种情况。

如何为块编写单元测试的问题.config仍然存在。让我们将我的问题中的朴素代码调整为.config实际需要的场景。以下代码片段注入提供程序依赖项而不是服务依赖项:

angular.module('myModule', []).config(['$httpProvider', function($httpProvider) {
    $httpProvider.useApplyAsync(true);
}]);

describe('myModule', function() {
    it('configures the $http service to combine response processing via $applyAsync', inject(function($httpProvider) {
        angular.module('myModule');
        expect($httpProvider.useApplyAsync()).toBeTruthy();
    }));
});
Run Code Online (Sandbox Code Playgroud)

这次执行'myModule'是正确的。然而,与我的问题中的尝试类似的单元测试仍然不正确。现在 Karma 给了我以下错误:

Error: [$injector:unpr] Unknown provider: $httpProviderProvider <- $httpProvider
Run Code Online (Sandbox Code Playgroud)

这个神秘的错误来自inject作为第二个参数传递给 的it。注意如何Provider口吃。这是由于inject正在寻找 的提供商$httpProvider而引起的。我们可以称之为“元提供者”。这些东西在 Angular 框架中不存在,但inject无论如何都在尝试,因为它希望您只请求服务依赖项。服务确实有提供者,例如,$http$httpProvider.

因此inject(全名: ,此处全局可用)不是在测试用例中angular.mock.inject获取的正确方法。正确的方法是使用( )$httpProvider定义一个匿名模块配置函数,该函数关闭一个我们可以捕获提供程序的变量。这是有效的,因为可以在配置时注入提供程序(请参阅mzulch 答案底部的链接以及我自己对其他问题的回答,了解有关配置时间与运行时间的详细信息)。它看起来像这样:moduleangular.mock.module

var $httpProvider;

beforeEach(function() {
    module(function(_$httpProvider_) {
        // this is a .config function
        $httpProvider = _$httpProvider_;
    });
    // after this I can use inject() to make the magic happen
});
Run Code Online (Sandbox Code Playgroud)

我天真的测试用例中的另一个错误是我试图'myModule'通过调用来执行配置步骤angular.module('myModule')。出于测试用例的目的,我应该使用全局module( angular.mock.module) 来代替,最明智的做法是在beforeEach夹具中。总之,以下代码完成了这项工作:

describe('myModule', function() {
    var $httpProvider;

    beforeEach(function() {
        module(function(_$httpProvider_) {
            $httpProvider = _$httpProvider_;
        });
        module('myModule');
    });

    it('configures the $http service to combine response processing via $applyAsync', function() {
        inject();  // enforces all the module config steps
        expect($httpProvider.useApplyAsync()).toBeTruthy();
    });
});
Run Code Online (Sandbox Code Playgroud)

我选择将 放在inject()测试用例的开头,但我也可以将它放在 的末尾beforeEach。后一种方法的优点是我可以在一个地方编写调用inject,而无需在每个测试用例中重复它。这里实际采用的方法的优点是可以在以后的es 中甚至在单独的测试用例中将更多模块添加到注入器中。beforeEach

我将这个替代解决方案推送到GitHub上的新分支。