【问题标题】:How to mock ng-grid when unit testing a controller单元测试控制器时如何模拟 ng-grid
【发布时间】:2014-03-19 20:08:25
【问题描述】:

我目前正在尝试为现有代码块编写测试,但遇到了一个内部有嵌套 ng-grid 的控制器的问题。问题来自控制器试图在初始化时与网格交互。

测试软件
节点@0.10.14
业力@0.10.2
业力茉莉@0.1.5
karma-chrome-launcher@0.1.2

我的测试:

define(["angularjs", "angular-mocks", "jquery",
    "js/3.0/report.app",
    "js/3.0/report.controller",
    "js/3.0/report.columns"
],
    function(angular, ngMocks, jquery, oARModule, oARCtrl, ARColumns) {
        "use strict";

        describe("Report Center Unit Tests", function() {
            var oModule;

            beforeEach(function() {
                oModule = angular.module("advertiser_report");
                module("advertiser_report");
            });

            it("Advertiser Report Module should be registered", function() {
                expect(oModule).not.toBeNull();
            });

            describe("Advertiser Report Controller", function() {
                var oCtrl, scope;

                beforeEach(inject(function($rootScope, $controller, $compile) {
                    var el = document.createElement('div');
                     el.setAttribute('ng-grid','gridOptions');
                     el.className = 'gridStyle';
                    scope = $rootScope.$new();
                    $compile(el)(scope);
                    oCtrl = $controller('ARController', {
                        $scope: scope
                    });
                }));

                it("Advertiser Report controller should be registered", function() {
                    expect(oCtrl).not.toBeNull();
                });
            });
        });
    });

您将看到我尝试在哪里创建和编译具有 ng-grid 属性的元素。如果不这样做,我会收到以下错误:

TypeError: 无法读取未定义的属性“列”

这是控制器尝试调用类似
的结果 $scope.gridOptions.$gridScope.columns.each

所以我添加了一个带有ng-grid属性的div的创建,得到了一个新的错误:

TypeError: 无法设置未定义的属性“gridDim”

所以,我尝试在 $controller 调用之前添加 scope.gridOptions,但这让我回到了最初的错误。我一直在寻找在不重写控制器和/或模板的情况下完成这项工作的方法,因为它们目前在生产中正常工作。

【问题讨论】:

  • 您是否尝试过在编译网格之前注入控制器?如果网格依赖于控制器范围内的东西,那么控制器必须有机会在网格尝试访问它之前将所有这些东西放在范围内,这对我来说是有意义的。
  • 网格不访问控制器,反之亦然。这就是我试图模拟这种依赖关系的原因。
  • 网格访问控制器定义的'gridOptions'范围属性,对吗?
  • 对,如果您阅读了我的问题的结尾,您会注意到我尝试将 gridOptions 添加到 scope 属性,这让我回到了原来的错误。
  • 看看这个 plunker:plnkr.co/edit/uTC1d0?p=preview 它有一个工作规范和一个失败的规范。唯一的区别是控制器被实例化的顺序。如果它在网格编译之前完成,它会起作用,因为控制器有机会在网格尝试在 $compile 期间访问它们之前定义范围属性。

标签: angularjs unit-testing ng-grid


【解决方案1】:

您的(主要!)问题是控制器正在对视图进行假设。它不应该知道,因此不应该与 ng-grid 交互。控制器应该是独立于视图的!这种质量(和依赖注入)使控制器具有高度可测试性。控制器应该只更改 ViewModel(即它的 $scope),并在测试中验证 ViewModel 是否正确。

否则会违反 MVVM 范式和最佳实践。

如果您觉得必须从控制器访问视图(即指令、DOM 元素等),那么您可能做错了什么。

【讨论】:

  • 刚刚注意到这个问题是 6 个月大。为什么它会出现在我的提要中? :)
  • 您的观点非常有效,我们在意识到编写测试不起作用后实际上对其进行了重构。
【解决方案2】:

第二个Failing测试的问题是gridOptions和myData在编译之前没有定义。注意 2 个语句的顺序。

通过 oCtrl = $controller('MainCtrl', { $scope: $scope }); $编译(榆树)($范围);

失败

$compile(elm)($scope);
oCtrl = $controller('MainCtrl', { $scope: $scope });

在这两种情况下,您都尝试使用相同的 html

elm = angular.element('<div ng-grid="gridOptions" style="width: 1000px; height: 1000px"></div>');

我建议你摆脱

oCtrl = $controller('MainCtrl', { $scope: $scope });

操纵并改用以下 HTML 元素

elm = angular.element('<div ng-controller="MainCtrl" 
      ng-grid="gridOptions" style="width: 1000px; height: 1000px"></div>');

通知 ng-controller="MainCtrl"

所以最后的故事是你需要在某个地方定义gridOptionsngGrid 可以访问它。并确保 gridOptions 依赖 控制器中的代码在 $timeout 中延迟。

另外看看 app.js 的细微变化

$timeout(function(){
    //your gridOptions dependent code
    $scope.gridOptions.$gridScope.columns.each(function(){
      return;
    });  
  });

这里是working plnkr

【讨论】:

  • 谢谢!我们已经重构了我们使用 ngGrid 的方式,所以它现在有点争议,但我现在明白为什么我的测试没有工作,所以谢谢你。
猜你喜欢
  • 1970-01-01
  • 2012-02-27
  • 1970-01-01
  • 1970-01-01
  • 2013-09-25
  • 2015-03-15
  • 2014-07-30
  • 2012-08-01
  • 1970-01-01
相关资源
最近更新 更多