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

时间:2016-08-11 15:13:25

标签: javascript angularjs karma-runner karma-jasmine angular-mock

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

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

可以找到包含上述测试代码的完整,可立即运行的Angular项目on GitHub。如果您知道如何处理此场景,请在此处回答Stack Overflow。如果您还向GitHub仓库提交拉取请求,则可获得奖励。

2 个答案:

答案 0 :(得分:5)

如果您的初始化需要注入服务,请使用run而不是configconfig函数只能接收提供者和常量作为参数,而不是$httprelevant docs)等实例化服务。

angular.module('myModule', []).run(['$http', '$log', function($http, $log) {
    ...
}]);

初始化您的模块以进行测试

beforeEach(module('myModule'));

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

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

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

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

答案 1 :(得分:3)

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

如何为.config块编写单元测试的问题仍然存在。让我们将我的问题中的naieve代码改编为实际保证.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();
    }));
});

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

Error: [$injector:unpr] Unknown provider: $httpProviderProvider <- $httpProvider

此加密错误来自inject,它作为第二个参数传递给it。请注意Provider如何结结巴巴。这是因为inject正在寻找 $httpProvider 的提供商。我们称之为“提供商”#34;这些事情并不存在于Angular框架中,但inject正在尝试它,因为它希望你只要求服务依赖。服务确实有提供商,例如$http$httpProvider

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

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

我的naieve测试用例中的另一个错误是我尝试通过调用'myModule'来执行angular.module('myModule')配置步骤。出于测试用途的目的,我应该使用全局moduleangular.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();
    });
});

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

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