AngularJS单元测试 - 用于注入依赖项的各种模式

rav*_*shi 6 unit-testing jasmine angularjs karma-jasmine

我是单元测试的新手,主要是从我找到的例子中学习.问题是我看到了很多不同的模式,很难理解它们之间的差异.以及如何将这些模式组合到各种用例中.下面是一个这样的模式:

    var $rootScope, $window, $location;
    beforeEach(angular.mock.module('security.service', 'security/loginModal.tpl.html'));

    beforeEach(inject(function(_$rootScope_, _$location_) {
        $rootScope = _$rootScope_;
        $location = _$location_;
    }));

    var service, queue;
    beforeEach(inject(function($injector) {
        service = $injector.get('security');
        queue = $injector.get('securityRetryQueue');
    }));
Run Code Online (Sandbox Code Playgroud)

因此,从这种模式中,我发现Angular核心服务/提供者应该注入下划线模式,其他第三方依赖项或我自己的依赖项应该使用$ injector.get()模式完成.这有效吗?我注意到我可以使用Angular核心服务执行$ injector.get()并且它仍然可以工作,所以也许只是这样做的惯例?另外,'security/loginModal.tpl.html'的重点是beforeEach(angular.mock.module('security.service', 'security/loginModal.tpl.html'));什么?我知道它是一个添加到模板缓存的HTML模板但是angular.mock.module做了什么呢?

我也看到过这种不太常见的模式,它会在上面的逻辑中抛出一个小扳手:

    beforeEach(inject(function($injector, _$location_) {
        security = $injector.get('security');
        $location = _$location_;
    }));
Run Code Online (Sandbox Code Playgroud)

如果我可以像注入$ location这样的注入回调添加服务,这似乎是一种更简单的引用依赖关系的方式.我为什么不这样做?

这是另一种模式:

    beforeEach(function() {
        module('security.service', function($provide) {
            $provide.value('$window', $window = jasmine.createSpyObj('$window', ['addEventListener', 'postMessage', 'open']));
        });

        inject(function(security) {
            service = security;
        });
    });
Run Code Online (Sandbox Code Playgroud)

根据我的理解,这种模式的重点是使用模拟的$ window初始化"security.service"模块.这是有道理的,但我如何使用以前的模式适应这种模式?即如何模拟'security/loginModal.tpl.html',如何注入我的Angular核心依赖项+我的其他依赖项?

最后,我可以和不能在嵌套的描述和注入块中注入什么?假设我无法将模拟服务复制到我正在测试的模块中,这是否安全?那么我可以注入什么以及用例是什么?

如果有一个确定的AngularJS单元测试初始化​​文档源可以帮助回答这些问题,请指出.

Phi*_*hil 3

我发现 Angular 核心服务/提供程序应该使用下划线模式注入,而其他第 3 方依赖项或我自己的依赖项应该使用 $injector.get() 模式完成

您可以使用其中任何一个。下划线模式只是一种避免与同名局部变量发生冲突的便捷方法考虑以下

var $rootScope, myService, http; // these are local variables

beforeEach(inject(function(_$rootScope_, _myService_, $http) {
    $rootScope = _$rootScope_; // underscores to avoid variable name conflict
    myService = _myService_; // same here with your custom service
    http = $http; // local variable is named differently to service
}));
Run Code Online (Sandbox Code Playgroud)

如果我可以像此代码对 $location 所做的那样将服务添加到注入回调中,那么这似乎是引用依赖项的更简单的方法。为什么我不应该这样做?

你应该 :)


另外,“security/loginModal.tpl.html”的意义是什么beforeEach(angular.mock.module('security.service', 'security/loginModal.tpl.html'));

据我所知,除非你有一个具有该名称的实际模块,例如

angular.module('security/loginModal.tpl.html', [])
Run Code Online (Sandbox Code Playgroud)

这会失败。angular.mock.module只能传递模块名称、实例或匿名初始化函数。


我如何模拟“security/loginModal.tpl.html”

理想情况下,你不应该这样做。单元测试应该测试代码的 API...交互点,通常由对象上可公开访问的方法和属性定义。

如果您只是想阻止 Karma 尝试通过 HTTP 加载模板(通常来自指令测试),您可以使用模板预处理器,例如karma-ng-html2js-preprocessor


最后,我可以和不能在嵌套描述和它阻止中注入什么?可以安全地假设我无法将模拟服务重新注入到我正在测试的模块中吗?那么我可以注入什么以及有哪些用例呢?

angular.mock.inject您几乎可以在任何地方运行(通常beforeEach是 和it)。模拟服务只能在模块或匿名模块初始化函数中配置(如您的示例中的 和$provide$window,并且通常在您自己的模块(即“security.service”)之后配置,以便通过将其替换为覆盖真实服务注射器。一旦运行inject(),您就无法追溯地用模拟替换服务。