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

标签 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 核心服务/提供程序应该使用下划线模式注入(inject),而其他第 3 方依赖项或我自己的依赖项应该使用 $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 所做的那样将服务添加到注入(inject)回调中,那么这似乎是引用依赖项的更简单的方法。为什么我不应该这样做?

这是另一种模式:

    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”,如何注入(inject)我的 Angular 核心依赖项 + 我的其他依赖项?

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

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

最佳答案

I've gleaned that Angular core services/providers should be injected with the underscore pattern where as other 3rd party dependencies or my own dependencies should be done using the $injector.get() pattern

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

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

If I can just add services to the inject callback like this code does with $location, that seems like a simpler way of referencing dependencies. Why should I not do this?

你应该:)

<小时/>

Also, what is the point of 'security/loginModal.tpl.html' in beforeEach(angular.mock.module('security.service', 'security/loginModal.tpl.html'));?

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

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

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

<小时/>

how do I mock 'security/loginModal.tpl.html'

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

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

<小时/>

Lastly, what can I and can't inject in nested describe and it blocks? Is it safe to assume I can't retro-inject mocked services to the module I'm testing. So then what can I inject and what are the use cases?

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

关于AngularJS 单元测试 - 注入(inject)依赖项的各种模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32582000/

相关文章:

angularjs - $compile 不编译 Karma/Jasmine 中的模板

unit-testing - TestBed:通过模块导入时组件不编译

javascript - 使用方法从 Angular 服务访问数据

javascript - 创建一个获取每个数组项并整理它的 JavaScript 对象

java - Maven Surefire 适当使用Exclude

reactjs - MUI V5 React 单元测试未触发日期选择器处理程序

jasmine - 如何使用 jasmine-report 和 Protractor 仅生成一个 XML 结果

jquery - 使用 $http 或 $.ajax 将对象数组发送到 MVC4 Controller

AngularJS 工厂在 ng-class 内的 body 标签不起作用

javascript - 模拟 API 调用时未填充 Redux 存储