Angular 1.5单元测试组件,同时忽略子组件

时间:2016-12-20 12:55:50

标签: angularjs unit-testing mocking components

我正在尝试为Angular 1.5中的组件编写单元测试。我想对该组件及其dom节点进行单元测试。该组件包含一个非常复杂的子组件。

我的目标是在不编译子组件的情况下对外部组件进行单元测试。

由于我也想测试DOM,因此使用$ componentController进行此测试是不够的。

以下是我想要实现的一个简短示例:

组件代码:

angular.module('app').component('myComponent', {
  bindings: {
    username: '<',
  },
  template: `
    <span>{{ $ctrl.username }}</span>
    <my-complex-component />
  `
  controller: function () {}
});

my-component的单元测试:

it('my-component should render username', function () {
  var template = '<my-component username="username"></my-component>',
    element,
    scope,
    date;

  scope = $rootScope.$new();
  scope.username = 'John';

  element = $compile(template)(scope);
  scope.$digest();

  username = element.find('span');
  expect(username.text()).to.be.equal('John');
});

我的复杂组件不应该被实例化。它应该在模板中抵制。在单元测试中创建元素应该导致

<span>John</span>
<my-complex-component />

有没有办法做到这一点?

4 个答案:

答案 0 :(得分:4)

Here is a very good explanation如何测试组件而不在当前组件下渲染整个树。

你想要的是什么叫

  

浅渲染(避免渲染子组件的整个模板)

并且Angular 1.5不提供开箱即用的功能。

如果您查看上面的链接,Wojciech Zawistowski将展示如何使用下面的帮助程序来实现浅层渲染。

export function componentSpyOn(name) {
  function componentSpy($provide) {
    componentSpy.bindings = [];

    $provide.decorator(name + 'Directive', ($delegate) => {
      let component = $delegate[0];

      component.template = '';
      component.controller = class {
        constructor() {
          componentSpy.bindings.push(this);
        }
      };

      return $delegate;
    });
  }

  return componentSpy;
}

答案 1 :(得分:2)

您可以使用$compileProvider.directive覆盖您的子组件,即使它是一个组件,因为组件是一种特殊的指令。

在您的单元测试中,使用与您的子组件同名的编译器注册一个新组件,但不提供控制器并将模板设置为空字符串。这将导致组件没有任何逻辑/复杂性:

beforeEach(module('app', function($compileProvider){
    $compileProvider.directive('myComplexComponent', function (){
        return {
            priority: 9999,
            terminal: true,
            template: ''
        };
    });
}));

因此,生成的html将是您的外部组件单元测试的最佳选择:

<span>John</span>    

答案 2 :(得分:0)

有一种方法可以测试它,但最终结果将是:

<span>John</span>
<ng-transclude></ng-transclude>

组件代码:

  angular.module('app').component('myComponent', {
  enter code herebindings: {
    username: '<',
  },
  template: `
    <span>{{ $ctrl.username }}</span>
    <ng-transclude></ng-transclude>
  `,
  transclude: true, // Added property
  controller: function () {}
});

如您所见,我删除了<my-complex-component>,并添加了<ng-transclude>

这意味着您可以从外部添加信息,并将其注入ng-transclude的位置。

例如,在你的主html中:

<my-component>
    <my-complex-component></my-complex-component>
</my-component>

将以您想要的方式从DOM开始:

<span>John</span>
<my-complex-component></my-complex-component>

之后你的测试应该有效。

我希望这个答案是你想要的,它会帮助你。

答案 3 :(得分:0)

您可以使用空模块覆盖单元测试中的子组件模块定义。

// Unit Test Parent component
describe('Unit Testing: myComponent Component', () => { 

  beforeEach(() => {
    // overwrite module
    angular.module('myComplexComponent', []);
    module('myComponent');
  });
});

现在,父组件仍会尝试注入和呈现子组件,但由于此子组件完全为空,因此不会发生任何事情。