【问题标题】:Isolation with Angular on karma jasmine在业力茉莉花上使用 Angular 进行隔离
【发布时间】:2015-09-20 13:12:52
【问题描述】:

我有一组分布在 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 中一样,它们通过了相同的测试。

我当然可以做一个测试命令并绕过它,但我的目标是找到一种方法来隔离测试,而不用担心其他测试的副作用。

【问题讨论】:

  • 是的,问题不是模拟模块依赖关系,更多的是在之前和下一次测试中测试了完整模块,它仍然加载。所以我有一个依赖指令,我不关心给定的测试试图点击一些我不想服务的html模板等等

标签: javascript angularjs karma-jasmine


【解决方案1】:

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

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

看到这个updated fiddle 与您的original fiddle 相比有两个 变化。

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

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

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

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

ng-click 示例或article 为例,概述我们如何利用多个同名指令。

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

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


装饰baz

testModule 的 beforeEach 中,替换以下内容:

$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', [])怎么样?

你不应该在你的规范中调用angular.module('name', []),除非你碰巧使用了angular-module gist。即便如此,在测试领域对你也没有多大帮助。

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

此外,parentModule 中的 baz 指令定义将自动在您的 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. 

因此,即使如果我们在您的规范中终止 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

【讨论】:

  • 伟大而完整的答案。非常感谢
猜你喜欢
  • 2014-12-12
  • 2017-01-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-03
  • 1970-01-01
  • 2020-02-22
  • 1970-01-01
相关资源
最近更新 更多