在 karma 茉莉花上与 Angular 隔离

Isolation with Angular on karma jasmine

我有一组分布在 2 个模块中的测试。

第一个模块有测试,对于它的依赖项,我声明模拟来测试它,而不受其依赖模块的任何影响,如下所示:

        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) {

            });

    })

第二个模块有一系列测试,当我 运行 只有它们时,所有测试都通过了。

        beforeEach(function(){

            angular.mock.module('module1');

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

            });

    })

当 运行 和 f(运行 只有它们)工作时,两个测试都有效,但是当我 运行 整个测试套装时,我得到错误,特别是关于模块声明或 $httpBackend.

如何让 jasmine 运行 每次测试都好像它们是唯一的测试一样?

看来我在每次测试中都弄乱了 angular/modules/$httpBackEnd,并且在它开始新测试时正在传播更改。

更新 1 我有一个 jsFiddle 代表这个问题。 问题的结构是:

在 JSFiddle 上,有关 $httpBackend 的错误没有任何内容可刷新是因为从未命中 expectedGet 的请求,并且由于先前加载的空模块而从未命中

重要的是要注意测试的顺序是唯一与失败相关的事情,因为在这个 JSFiddle 中他们通过了相同的测试。

我当然可以制定测试顺序并绕过它,但我的目标是找到一种方法来隔离进行测试,而不必担心其他测试的副作用。

您遇到的问题是由于 $compileProvider 处理使用预先存在的名称注册的新指令的方式的性质。

简而言之; 您没有覆盖旧指令,您正在创建一个同名的辅助指令。因此,原始实现 运行s 并尝试获取 baz.html$httpBackend 抛出,因为您没有为该调用设置期望。

看到这个 updated fiddle that did two changes from your original fiddle

  1. 不要将 parentModule 注入到您的 childModule 规范中。不需要该行,这是您看到这些错误的部分原因。哦,angular.module 在考验之地是 邪恶的 。尽量不要使用它。
  2. 修饰 原始指令,如果你想使用与原始指令相同的名称滚动,或者将其命名为其他名称。 I我选择在 fiddle 中将其命名为其他名称,但我在答案末尾提供了代码以显示 装饰器方式。

这是以下场景中发生的情况的屏幕截图:

  • 模块 A 注册了一个名为 baz 的指令。
  • 模块 B 依赖于模块 A
  • 模块 B 注册了一个名为 baz 的指令。

正如您可能想象的那样,为了让模块系统不通过让人们覆盖彼此的指令来 insta-gib 本身 - $compileProvider 将简单地注册 另一个指令同名两者都会运行

以此 ng-click example or this article 来概述我们如何利用同名的多个指令。

请参阅下面随附的屏幕截图,了解您的情况。

7173 行的代码是您可以实施我在回答开头提到的解决方案 #2 的地方。


装修中baz

在您的 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
    };
});

有了这个:

$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


调用 angular.module('parent', []) 怎么样?

You should not call angular.module('name', []) in your specs, unless you happen to be using the angular-module gist. And even then it's not doing much for you in the land of testing.

只使用 .mock.modulewindow.module,否则你 杀死与指定模块相关的即将到来的规范,因为你已经有效地杀死了spec 运行.

其余部分的模块定义

此外,由于以下原因,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. 

因此,即使 if 我们在您的规范中终止了 angular.module('parent', []) 调用,原始 baz 定义 仍然 正在加载。

因此,由于 $compileProvider 支持多个具有相同名称的指令的性质,HTTP 请求失败,这就是您的规范套件失败的原因。


此外,作为最后一点;您正在 beforeEach 块中配置 undefined 模块。如果目标是配置测试模块,那你就错了。

语法如下:

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

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

这可以在我发布的屏幕截图中看到。您的模拟 baz 定义的 $$moduleName 属性 是 undefined,而我假设您希望它是 child.