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

6

我刚接触单元测试,主要是从一些例子中学习。问题是我看到了太多不同的模式,很难理解它们之间的区别,以及如何将这些模式组合应用于各种用例。以下是其中一种模式:

    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(),它仍然有效,所以也许这只是传统做法?此外,在beforeEach(angular.mock.module('security.service','security/loginModal.tpl.html'));中,“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;
        });
    });

从我的理解来看,这个模式的重点是使用模拟$window初始化“security.service”模块。这是有道理的,但我该如何将这个模式与之前的模式结合起来呢?也就是说,我该如何模拟“security/loginModal.tpl.html”,如何注入我的Angular核心依赖项和其他依赖项?
最后,我在嵌套的describe和it块中可以注入什么,不能注入什么?可以肯定的是,我不能向正在测试的模块中重复注入模拟服务。那么,我可以注入什么,有哪些用例呢?
如果有一个定义明确的文档来源,可以帮助回答这些问题,请指引我去阅读它。
1个回答

3
我了解到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
}));

如果我可以像这段代码一样在注入回调中添加服务,那么这似乎是一种更简单的引用依赖项的方式。为什么我不应该这样做呢?
你应该这样做 :)
另外,在`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


最后,我在嵌套的describe和it块中可以注入哪些内容,哪些内容不能注入?假设向我正在测试的模块中注入mocked services是不安全的,那么我可以注入哪些内容,有哪些使用情况?

你几乎可以在任何地方运行angular.mock.inject(通常是在beforeEachit中)。模拟服务应该只在模块或匿名模块初始化函数中配置(就像你的例子中使用$provide$window),通常在你自己的模块之后(即"security.service"),以便通过替换注入器中的真实服务来覆盖它们。一旦你运行了inject(),就无法用mock替换一个已经存在的服务。


谢谢!我现在更好地理解了所有内容。 - ravishi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接