Angular 1.5 单元测试控制器,需要父组件的控制器

Posted

技术标签:

【中文标题】Angular 1.5 单元测试控制器,需要父组件的控制器【英文标题】:Angular 1.5 unit test controller that requires parent component's controller 【发布时间】:2016-10-31 21:19:25 【问题描述】:

我正在尝试对需要父组件和来自另一个模块的控制器的 AngularJS 1.5(带有 Webpack)组件的(子)控制器进行单元测试。

子控制器结构:

function ChildController () 
  var vm = this;

  vm.searchText = '';

  vm.submit = function() 
    var data = ;
    data['srch'] = vm.searchText;
    vm.parentCtrl.submitTextSearch(data);
  ;


module.exports = ChildController;

子组件:

var template = require('./child.html');
var controller = require('./child.controller');

var childComponent = 
  require: 
    parentCtrl: '^parent'
  ,
  template: template,
  controller: controller,
  controllerAs: 'vm'
;

module.exports = childComponent;

所以我想做的是模拟出 childController 的 submit() 函数中所需的 parentCtrl。我一直无法找到如何真正做到这一点。我找到了一些类似的子父指令解决方案并尝试了这些,例如通过this child-parent directive example 中描述的假HTML 元素注入父控制器,基本相同*** solutions 没有结果。我的问题至少不同之处在于子控制器和父控制器位于不同的模块中。而且我认为范围技巧不是 Angular 1.5 风格的?

没有失败的模拟尝试的 Jasmine 测试框架:

describe('child component', function() 
  describe('child controller', function() 
    var controller;
    beforeEach(angular.mock.module('child'));
    beforeEach(inject(function(_$componentController_) 
      controller = _$componentController_('child');
    ))
    it('should work', function() 
      controller.searchText = "test";
      controller.submit();
    )
  )
)

这导致TypeError: Cannot read property 'submitTextSearch' of undefined。我应该怎么做才能模拟父控制器?由于我在 Angular 方面的经验有限,我没有想法。

【问题讨论】:

【参考方案1】:

使用下面的代码将初始化它,请检查 Jasmine 单元测试Plunker

var ctrP = $componentController('parentComp');
var ctrl = $componentController('childComp', , 
  parentCtrl: ctrP
);

你的测试用例应该如下图所示:

'use strict';

describe('component: heroDetail', function() 
  var $componentController, $compile, $rootScope;

  beforeEach(module('plunker'));
  beforeEach(inject(function(_$componentController_) 
    $componentController = _$componentController_;
  ));

  it('should expose a `hero` object', function() 
    var ctrP = $componentController('parentComp');
    console.log(ctrP);
    var ctrl = $componentController('childComp', , 
      parentCtrl: ctrP
    );
    console.log(ctrl);
    ctrl.submit('some data');
    expect(ctrl.parentCtrl.searchText).toEqual('some data');

  );
);

【讨论】:

【参考方案2】:

1.解决方案

在您的测试中,使用新范围实例化父控制器:

mainScope = $rootScope.$new();
$controller('ParentController', $scope: mainScope);

然后在您的子控制器中,使用之前实例化的作用域实例化一个新作用域:

childScope = mainScope.$new();
$controller('ChildController', $scope: childScope);

来自AngularJS documentation的示例:

describe('state', function() 

  var mainScope, childScope, grandChildScope;

  beforeEach(module('myApp'));

  beforeEach(inject(function($rootScope, $controller) 
      mainScope = $rootScope.$new();
      $controller('MainController', $scope: mainScope);
      childScope = mainScope.$new();
      $controller('ChildController', $scope: childScope);
      grandChildScope = childScope.$new();
      $controller('GrandChildController', $scope: grandChildScope);
  ));

  it('should work', function() 
      grandChildScope.searchText = "test";
      grandChildScope.submit();
  );
);

2.解决方案

子控制器结构:

function ChildController () 
  var vm = this;

  vm.searchText = '';

  vm.submit = function() 
    var data = ;
    data['srch'] = vm.searchText;
    vm.parentCtrl.submitTextSearch(data);
  ;


module.exports = ChildController;

子组件:

var template = require('./child.html');
var controller = require('./child.controller');

    var childComponent = 
      bindings: 
         searchText: 'test'
      ,
      template: template,
      controller: controller,
      controllerAs: 'vm'
    ;

module.exports = childComponent;

var ChildController = $componentController('childComponent', null, ...);
ChildController.$onInit();
expect(ChildController.searchText).to.equal('test');
expect(ChildController.submit()).to.equal('*expected result value should come here*');

参考:

AngularJS documentation - Testing Controllers

AngularJS documentation - $componentController

Unit Testing Angular Components with $componentController

【讨论】:

【参考方案3】:

在您的情况下,您将添加 parentCtrl 作为组件的依赖项,因此为了测试它,您还必须模拟父组件并将其分配给控制器。因此,您需要执行以下操作:

beforeEach(inject(function(_$componentController_) 
  controller = _$componentController_('child');
  parentCtrl = _$componentController_('parent');
  controller.parentCtrl = parentCtrl;
))

【讨论】:

我已经尝试过了,但对我不起作用。仍然得到“无法找到指令''所需的控制器''!”。 你确定你的业力配置吗?可能有些文件没有被加载。

以上是关于Angular 1.5 单元测试控制器,需要父组件的控制器的主要内容,如果未能解决你的问题,请参考以下文章

Angular 1.5 组件通过 ngRoute 更新父控制器

单元测试时如何在 Angular 1.5 中向 $componentController 添加依赖项

如何在 Angular 1.5 中从父控制器访问组件方法

angular 1.5 组件调用父作用域方法

在 Angular 1.5 组件中使用 $emit

Angular 2 单元测试数据从父组件传递到子组件