简体   繁体   English

与业力茉莉花上的角度隔离

[英]Isolation with Angular on karma jasmine

I have a set of tests that are spread across 2 modules. 我有一组分布在两个模块中的测试。

The first module has tests and for it's dependencies i declare mocks to test it without any influence from it's dependent module, like so: 第一个模块有测试,对于它的依赖,我声明模拟测试它而不受其依赖模块的任何影响,如下所示:

        beforeEach(function(){
            angular.mock.module(function ($provide) {
                $provide.value("mockServiceFromModule1", mockServiceFromModule1);
                $provide.value("mockServiceFromModule2", mockServiceFromModule2);
            });

            angular.module('module1', []);
            angular.module('module2', []);
            angular.mock.module('moduleThatIAmTesting');

            angular.mock.inject(function (_$rootScope_, _$q_, _$httpBackend_, ..., somethingFromTestModule) {

            });

    })

The second module has a series of tests and all of them pass when i run only them. 第二个模块有一系列测试,当我只运行它们时它们全部通过。

        beforeEach(function(){

            angular.mock.module('module1');

            angular.mock.inject(function (_$rootScope_, _$q_, _$httpBackend_, ..., somethingFromModule1) {

            });

    })

Both tests when run with f(Running only them) works, but when i run the whole test suit i get errors, specially regarding module declaration or $httpBackend . 使用f(仅运行它们)运行时的两个测试都有效,但是当我运行整个测试套装时,我会收到错误,特别是关于模块声明或$httpBackend

How can i make jasmine run each test as if they were the only tests? 我如何使茉莉花运行每个测试,好像它们是唯一的测试?

It seems i am messing with the angular/modules/$httpBackEnd on each test and the changes are being propagated when it starts a new test. 看来我在每个测试中都在使用angular / modules / $ httpBackEnd,并且在开始新测试时会传播更改。

Update 1 I have a jsFiddle representing the issue . 更新1我有一个代表问题的jsFiddle The structure of the problem is : 问题的结构是:

  • Some test is ran with a mock dependant module 一些测试是使用模拟依赖模块运行的
  • Later another test wants to test the actual mocked module 后来另一个测试想要测试实际的模拟模块
  • Since the first moldule was already loaded we can't overwritte it and the test fails. 由于第一个moldule已经加载,我们无法覆盖它,测试失败。

On the JSFiddle the error about $httpBackend without nothing to flush is because the request for the expectedGet is never hit, and it's never hit because of the previously loaded empty module 在JSFiddle上,关于$ httpBackend的错误没有什么要刷新是因为对expectGet的请求永远不会被命中,并且由于先前加载的空模块而从未命中

It's important to notice that the ORDER of the tests is the only thing relevant to failing as in this JSFiddle with the same tests they pass. 重要的是要注意测试的ORDER是唯一与失败相关的东西,因为在这个JSFiddle中它们通过了相同的测试。

I could of course make a tests order and bypass this but i am aiming to find a way to do the tests with isolation without worrying about other tests side effects. 我当然可以做一个测试命令并绕过这个,但我的目标是找到一种方法来进行隔离测试而不必担心其他测试副作用。

The problem you are experiencing is due to the nature of how the $compileProvider handles a new directive being registered with a pre-existing name. 您遇到的问题是由于$compileProvider如何处理使用预先存在的名称注册的新指令的性质。

In short; 简而言之; You are not overriding your old directive , you are creating a secondary one with the same name. 您没有覆盖旧指令 ,而是创建具有相同名称的辅助指令 As such, the original implementation runs and tries to grab baz.html and $httpBackend throws as you have not set up an expectation for that call. 因此,原始实现运行并尝试抓取baz.html$httpBackend抛出,因为您尚未设置该调用的期望。

See this updated fiddle that did two changes from your original fiddle . 看到这个更新的小提琴 ,从你原来的小提琴做了两个改变。

  1. Do not inject the parentModule to your childModule spec. 不要将parentModule注入childModule规范。 That line is not needed and it is part of the reason you are seeing these errors. 不需要该行,这是您看到这些错误的部分原因。 Oh and angular.module is evil in the land of tests. 哦, angular.module在测试之地是邪恶的。 Try to not use it. 尽量不要使用它。
  2. Decorate the original directive if you wish to roll with the same name as the original one, or name it something else. 如果您希望使用与原始指令相同的名称滚动,则装饰原始指令,或将其命名为其他名称。 I've opted for naming it something else in the fiddle, but I have supplied code at the end of my answer to show the decorator way. 我选择在小提琴中命名别的东西,但我在答案的最后提供了代码以显示装饰者的方式。

Here's a screenshot of what happens in the following scenario: 以下是在以下场景中发生的事情的屏幕截图:

  • Module A registers a directive called baz . 模块A注册一个名为baz的指令。
  • Module B depends on module A . 模块B依赖于模块A.
  • Module B registers a directive called baz . 模块B注册了一个名为baz的指令。

As you can probably imagine, in order for the module system to not insta-gib itself by letting people overwrite eachothers directives - the $compileProvider will simply register another directive with the same name . 正如您可能想象的那样,为了让模块系统不要通过让人们覆盖eachothers指令来实现自己的操作 - $compileProvider将只注册另一个具有相同名称的 指令 And both will run . 两者都会运行

Take this ng-click example or this article outlining how we can leverage multiple directives with the same name. ng-click示例或本文为例,概述我们如何利用具有相同名称的多个指令。

See the attached screenshot below for what your situation looks like. 请参阅下面附带的屏幕截图,了解您的情况。 多DIR-同名

The code on lines 71 to 73 is where you could implement solution #2 that I mentioned in the start of my answer. 7173行的代码是您可以实现我在答案开头提到的解决方案#2的地方。


Decorating baz 装饰baz

In your beforeEach for testModule , replace the following: 在您的beforeEach for testModule ,替换以下内容:

$compileProvider.directive('baz', function () {
    return {
        restrict: 'E',
        template: '{{value}}<div ng-transclude></div>',
        controllerAs: 'bazController',
        controller: function ($scope, fooService) {
            $scope.value = 'baz' + fooService.get()
        },
        transclude: true
    };
});

With this: 有了这个:

$provide.decorator('bazDirective', function ($delegate) {
  var dir = $delegate[0];

  dir.template = '{{value}}<div ng-transclude></div>';

  dir.controller = function ($scope, fooService) {
      $scope.value = 'baz' + fooService.get();
  };

  delete dir.templateUrl;

  return $delegate;
});

jsFiddle showing the decorator approach jsFiddle显示装饰器方法


What about the call to angular.module('parent', []) ? angular.module('parent', [])的调用怎么样?

You should not call angular.module('name', []) in your specs, unless you happen to be using the angular-module gist. 您不应该在规范中调用angular.module('name', []) ,除非您碰巧使用angular-module gist。 And even then it's not doing much for you in the land of testing. 即便如此,在测试领域对你没有太大作用。

Only ever use .mock.module or window.module , as otherwise you will kill your upcoming specs that relate to the specified module, as you have effectively killed the module definition for the rest of the spec run . 只能使用.mock.modulewindow.module ,否则您 .mock.module与指定模块相关的即将推出的规范,因为您已经有效地杀死了其余规范运行的模块定义。

Furthermore, the directive definition of baz from parentModule will automatically be available in your testModule spec due to the following: 此外, parentModulebaz的指令定义将自动在testModule规范中提供,原因如下:

angular.module('parent', []).directive('baz', fn());

angular.module('child', ['parent']);

// In your test: 

module('child'); // Will automatically fetch the modules that 'child' depend on. 

So, even if we kill the angular.module('parent', []) call in your spec, the original baz definition will still be loaded. 所以, 即使我们杀angular.module('parent', [])在您电话的规格,原baz 定义还是被加载。

As such, the HTTP request flies off due to the nature of $compileProvider supporting multiple directives with the same name, and that's the reason your spec suite is failing. 因此,由于$compileProvider支持具有相同名称的多个指令的性质,HTTP请求飞走了,这就是您的规范套件失败的原因。


Also, as a last note; 另外,作为最后一点; You are configuring undefined modules in your beforeEach blocks. 您正在beforeEach块中配置undefined模块。 If the goal is to configure the module of your test, you are in the wrong. 如果目标是配置测试模块,则说明错了。

The syntax is as follows: 语法如下:

mock.module('child', function ($compileProvider, /** etc **/) {
});

// Not this:
mock.module('child'); 
mock.module(function ($compileProvider, /** etc **/) {
});

This can be seen in the screenshot I posted. 这可以在我发布的截图中看到。 The $$moduleName property of your mocked baz definition is undefined , whereas I am assuming you would want that to be child . 你模拟的baz定义的$$moduleName属性是undefined ,而我假设你希望它是child

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM