javascript - AngularJS单元测试: Initialize scope of a directive's controller

标签 javascript angularjs unit-testing

我有以下指令代码,使用带有“controller as”语法的单独 Controller :

'use strict';

angular.module('directives.featuredTable', [])

.controller('FeaturedTableCtrl',
['$scope',
function ($scope){
  var controller = this;

  controller.activePage = 1;
  controller.changePaginationCallback =
    controller.changePaginationCallback || function(){};
  controller.density = 10;
  controller.itemsArray = controller.itemsArray || [];
  controller.metadataArray = controller.metadataArray || [];
  controller.numberOfItems = controller.numberOfItems || 0;
  controller.numberOfPages = 1;
  controller.options = controller.options || {
    'pagination': false
  };

  controller.changePaginationDensity = function(){
    controller.activePage = 1;
    controller.numberOfPages =
      computeNumberOfPages(controller.numberOfItems, controller.density);

    controller.changePaginationCallback({
      'page': controller.activePage,
      'perPage': controller.density
    });
  };

  controller.getProperty = function(object, propertyName) {
    var parts = propertyName.split('.');

    for (var i = 0 ; i < parts.length; i++){
      object = object[parts[i]];
    }

    return object;
  };

  controller.setActivePage = function(newActivePage){
    if(newActivePage !== controller.activePage &&
      newActivePage >= 1 && newActivePage <= controller.numberOfPages){

      controller.activePage = newActivePage;
      controller.changePaginationCallback({
        'page': controller.activePage,
        'perPage': controller.density
      });
    }
  };

  initialize();

  $scope.$watch(function () {
    return controller.numberOfItems;
  }, function () {
    controller.numberOfPages =
      computeNumberOfPages(controller.numberOfItems, controller.density);
  });

  function computeNumberOfPages(numberOfItems, density){
    var ceilPage = Math.ceil(numberOfItems / density);
    return ceilPage !== 0 ? ceilPage : 1;
  }

  function initialize(){
    if(controller.options.pagination){
      console.log('paginate');
      controller.changePaginationCallback({
        'page': controller.activePage,
        'perPage': controller.density
      });
    }
  }
}]
)

.directive('featuredTable', [function() {
return {
  'restrict': 'E',
  'scope': {
    'metadataArray': '=',
    'itemsArray': '=',
    'options': '=',
    'numberOfItems': '=',
    'changePaginationCallback': '&'
  },
  'controller': 'FeaturedTableCtrl',
  'bindToController': true,
  'controllerAs': 'featuredTable',
  'templateUrl': 'directives/featuredTable/featuredTable.tpl.html'
};
}]);

您可以在 Controller 的开头看到我正在使用指令传递的属性或提供默认值来初始化其属性:

controller.activePage = 1;
controller.changePaginationCallback =
    controller.changePaginationCallback || function(){};
controller.density = 10;
controller.itemsArray = controller.itemsArray || [];
controller.metadataArray = controller.metadataArray || [];
controller.numberOfItems = controller.numberOfItems || 0;
controller.numberOfPages = 1;
controller.options = controller.options || {
  'pagination': false
};

最后,我执行 initialize(); 函数,该函数将根据选项执行回调:

function initialize(){
  if(controller.options.pagination){
    controller.changePaginationCallback({
      'page': controller.activePage,
      'perPage': controller.density
    });
  }
}

我现在正在尝试对该 Controller 进行单元测试(使用 karma 和 jasmine),并且我需要“模拟”指令传递的参数,我尝试了以下操作:

'use strict';

describe('Controller: featured table', function () {

  beforeEach(module('directives.featuredTable'));

  var scope;
  var featuredTable;
  var createCtrlFn;
  beforeEach(inject(function ($controller, $rootScope) {
    scope = $rootScope.$new();

    createCtrlFn = function(){
      featuredTable = $controller('FeaturedTableCtrl', {
        '$scope': scope
      });
      scope.$digest();
    };
  }));

  it('should initialize controller', function () {
    createCtrlFn();

    expect(featuredTable.activePage).toEqual(1);
    expect(featuredTable.changePaginationCallback)
      .toEqual(jasmine.any(Function));
    expect(featuredTable.density).toEqual(10);
    expect(featuredTable.itemsArray).toEqual([]);
    expect(featuredTable.metadataArray).toEqual([]);
    expect(featuredTable.numberOfPages).toEqual(1);
    expect(featuredTable.numberOfItems).toEqual(0);
    expect(featuredTable.options).toEqual({
      'pagination': false
    });
  });

  it('should initialize controller with pagination', function () {
    scope.changePaginationCallback = function(){};
    spyOn(scope, 'changePaginationCallback').and.callThrough();

    scope.options = {
      'pagination': true
    };

    createCtrlFn();

    expect(featuredTable.activePage).toEqual(1);
    expect(featuredTable.changePaginationCallback)
      .toEqual(jasmine.any(Function));
    expect(featuredTable.density).toEqual(10);
    expect(featuredTable.itemsArray).toEqual([]);
    expect(featuredTable.metadataArray).toEqual([]);
    expect(featuredTable.numberOfPages).toEqual(1);
    expect(featuredTable.numberOfItems).toEqual(0);
    expect(featuredTable.options).toEqual({
      'pagination': true
    });

    expect(featuredTable.changePaginationCallback).toHaveBeenCalledWith({
      'page': 1,
      'perPage': 10
   });
  });
});

并出现以下错误,这意味着范围未正确初始化:
预期的 Object({ pagination: false }) 等于 Object({ pagination: true })
在测试/spec/app/rightPanel/readView/historyTab/historyTab.controller.spec.js:56

最佳答案

模拟绑定(bind)并非易事 - 毕竟,很难真正知道编译和链接指令对传递给它的数据做了什么......除非您自己做!

Angular.js 文档提供了有关如何编译和链接单元测试指令的指南 - https://docs.angularjs.org/guide/unit-testing#testing-directives 。完成此操作后,您只需要从结果元素中获取 Controller (请参阅此处的 controller() 方法的文档 - https://docs.angularjs.org/api/ng/function/angular.element )并执行测试。 ControllerAs 在这里是无关紧要的 - 您将直接测试 Controller ,而不是操纵范围。

这是一个示例模块:

var app = angular.module('plunker', []);

app.controller('FooCtrl', function($scope) {
  var ctrl = this;

  ctrl.concatFoo = function () {
    return ctrl.foo + ' world'
  }
})
app.directive('foo', function () {
  return {
    scope: {
      foo: '@'
    },
    controller: 'FooCtrl',
    controllerAs: 'blah',
    bindToController: true,
  }
})

和测试设置:

describe('Testing a Hello World controller', function() {
  ctrl = null;

  //you need to indicate your module in a test
  beforeEach(module('plunker'));

  beforeEach(inject(function($rootScope, $compile) {
    var $scope = $rootScope.$new();
    var template = '<div foo="hello"></div>'
    var element = $compile(template)($scope)

    ctrl = element.controller('foo')

  }));

  it('should produce hello world', function() {
    expect(ctrl.concatFoo()).toEqual('hello world')
  });
});

(现场演示:http://plnkr.co/edit/xoGv9q2vkmilHKAKCwFJ?p=preview)

关于javascript - AngularJS单元测试: Initialize scope of a directive's controller,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28967430/

相关文章:

c# - 在 Asmx Web 服务单元测试中出错

unit-testing - 您使用哪些工具/库对您的 PL/PgSQL 进行单元测试

javascript - ASP.NET 阻止用户访问未捆绑的脚本

javascript - 火狐/Chrome扩展程序: how to open link when new tab is created?

javascript - 将 POST 答案拆分为不同的 jquery 值

javascript - Angular Js/ui-路由器/选项卡设置, Controller 被调用两次

python - 单元测试中的模拟 Django 命令类变量

javascript - jquery 动画和 .offset

javascript - Angular $broadcast 不起作用

javascript - 如何仅在输入内容时加载数据