AngularJS单元测试:初始化指令控制器的范围

时间:2015-03-10 15:16:00

标签: javascript angularjs unit-testing

对于使用带有“controller as”语法的分离控制器的指令,我有以下代码:

'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.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
    });
  }
}

我现在正试图对这个控制器进行单元测试(使用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
   });
  });
});

并且出现以下错误,意味着范围没有很好地初始化:
预期对象({pagination:false})等于Object({pagination:true})
at test / spec / app / rightPanel / readView / historyTab / historyTab.controller.spec.js:56

1 个答案:

答案 0 :(得分:0)

模拟绑定非常重要 - 毕竟,很难真正知道 编译和链接指令与传递给它的数据有什么关系......除非你自己动手做!

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

这是一个示例模块:

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