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

时间:2015-09-15 09:06:26

标签: angularjs unit-testing jasmine 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');
    }));

因此,从这种模式中,我发现应该使用下划线模式注入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_;
    }));

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

这是另一种模式:

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

        inject(function(security) {
            service = security;
        });
    });

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

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

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

1 个答案:

答案 0 :(得分:2)

  

我已经知道Angular核心服务/提供程序应该注入下划线模式,其他第三方依赖项或我自己的依赖项应该使用$ 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
}));
  

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

你应该:)

  

另外,beforeEach(angular.mock.module('security.service', 'security/loginModal.tpl.html'));中'security / loginModal.tpl.html'的重点是什么?

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

angular.module('security/loginModal.tpl.html', [])

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

  

如何模拟'security / loginModal.tpl.html'

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

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

  

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

您可以在任何地方(通常为beforeEachit)运行angular.mock.inject。模拟服务应仅在模块或匿名模块初始化函数中配置(如您的示例中$provide$window),并且通常您自己的模块之后(即“security.service”)以通过在注入器中替换它们来覆盖实际服务。运行inject()后,您无法使用模拟回溯主动替换服务。