【问题标题】:Unit test views - best practice单元测试视图 - 最佳实践
【发布时间】:2014-04-15 12:23:36
【问题描述】:

任何人都可以分享单元测试视图的经验吗?我阅读了很多关于如何使用视图进行单元测试的教程,但一切都有一些缺点。

我采用了以下方法。它有效,但我想知道是否有更好的方法来做到这一点。还有一些缺点,我稍后会解释。我也在用量角器进行 E2E 测试,但它们总是很慢,因此我将它们限制在最低限度。

这是我的控制器。它有两个绑定到其$scope 的变量,这些变量在视图中使用:

// test_ctrl.js
angular.module('app', [])
  .controller('TestCtrl', ["$rootScope", "$scope", function ($rootScope, $scope) {
    $scope.bar = "TEST";
    $scope.jobs = [
      {name: "cook"}
    ];
  }]);

视图将$scope.bar 放入<span> 并将$scope.jobs 数组放入ng-repeat 指令:

<!-- test.html the view for this controller -->
<span>
  Bar is {{bar || "NOT SET"}}
</span>
<ul>
  <li ng-repeat="job in jobs">{{job.name}}</li>
</ul>

这是测试:

describe('Controller: TestCtrl', function () {
  beforeEach(module('templates'));
  beforeEach(module('app'));

  var TestCtrl, $rootScope, $compile, createController, view, $scope;
  beforeEach(inject(function($controller, $templateCache, _$rootScope_, _$compile_, _$httpBackend_) {
    $rootScope = _$rootScope_;
    $scope = $rootScope.$new();
    $compile = _$compile_;

    createController = function() {
      var html = $templateCache.get('views/test.html');
      TestCtrl = $controller('TestCtrl', { $scope: $scope, $rootScope: $rootScope });
      view = $compile(angular.element(html))($scope);
      $scope.$digest();
    };
  }));

  it('should test the view', function() {
    createController();
    expect(view.find("li").length).toEqual(1)
    console.log($scope.jobs)
  });
});

beforeEach 函数中,我将设置控制器。 createController 函数(从测试本身调用)从$templateCache 中取出一个视图,用它自己的$scope 创建一个控制器,然后编译模板并触发一个$digest

模板缓存预填充了业力预处理器 ng-html2js

// karma.conf.js
...
preprocessors: {
  'app/views/*.html': 'ng-html2js'
}
...

用这种方法,我有一个小问题,还有一些疑问:

1.来自ng-repeat的对象中的其他 $$hashKey 键

我测试中的expect($scope.jobs).toEqual([{name: "cook"}]); 抛出错误:

Expected [ { name : 'cook', $$hashKey : '009' } ] to equal [ { name : 'cook' } ]

我知道ng-repeat 添加了这些键,但测试起来很愚蠢。我能想到的唯一方法是将控制器测试和视图测试分开。但是当我检查控制器内的jobs 数组时,$$hashKey 不存在。有什么想法,为什么会这样?

2。 $范围问题

当我第一次尝试这个时,我只将我的本地范围定义为$scope={} 而不是$scope = $rootScope.$new(),就像我在其他控制器测试中所做的那样。但是只有一个普通对象作为本地范围,我无法编译它($compile(angular.element(html))($scope); 抛出错误)。

我还认为将$rootScope 本身作为控制器的当前本地范围传递是否是个好主意。这是一个好方法吗?还是有什么缺点,我还没看到?

3.最佳做法

我很高兴知道其他人是如何在 AngularJS 中进行单元测试的。我认为必须对视图进行测试,因为在所有角度指令中,其中包含很多逻辑,我很高兴看到防水 ;)

【问题讨论】:

  • 值得注意的是,在 ng-repeat 指令中使用 track by 表达式可能会帮助您摆脱哈希键(并加快查找速度)。见codelord.net/2014/04/15/…
  • 这与单元测试无关,但是在查看了示例控制器之后,我想在此处留下这条评论,以供路过的程序员博学:不污染$scope 是一个好习惯使用控制器的局部变量。最好使用this.bar = "TEST",并在视图中使用ng-controller="TestCtrl as ctrl"ctrl.bar 引用它。

标签: javascript angularjs unit-testing testing


【解决方案1】:

我认为您正在做的是对视图进行单元测试的好方法。您问题中的代码对于希望进行单元测试视图的人来说是一个很好的方法。


1。 ng-repeat $$hashKey

​​>

不用担心数据。相反,测试各种操作的结果,因为这才是你真正关心的事情。所以,使用jasmine-jquery来验证创建控制器后DOM的状态,模拟click()s等之后的状态。


2。 $scope = $rootScope.$new() 没问题

$rootScopeScope 的实例,而 $rootScope.$new() 创建 ChildScope 的实例。使用 ChildScope 的实例进行测试在技术上更正确,因为在生产中,控制器范围也是 ChildScope 的实例。

顺便说一句,创建隔离范围的单元测试指令也是如此。当您 $compile 带有 ChildScope 实例的指令时,将自动创建一个隔离范围(这是 Scope 的一个实例)。您可以使用 element.isolateScope() 访问该隔离范围

// decalare these variable here so we have access to them inside our tests
var element, $scope, isolateScope;

beforeEach(inject(function($rootScope, $compile) {
  var html = '<div my-directive></div>';

  // this scope is an instance of ChildScope
  $scope = $rootScope.$new();

  element = angular.element(html);   
  $compile(element)($scope);
  $scope.$digest();

  // this scope is an instance of Scope
  isolateScope = element.isolateScope(); 
}));

3。 +1 测试视图

有人说用 Protractor 测试视图。当您想测试整个堆栈时,Protractor 非常棒:从前端到后端。但是,量角器很慢,单元测试很快。这就是为什么通过模拟依赖后端的应用程序的任何部分来使用单元测试来测试您的视图和指令是有意义的。

指令是高度可单元测试的。控制器较少。控制器可能有很多活动部件,这会使它们更难测试。出于这个原因,我赞成经常创建指令。结果是更易于测试的模块化代码。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-08
    相关资源
    最近更新 更多