【问题标题】:Unit testing Angular directive with templateUrl and triggerHandler使用 templateUrl 和 triggerHandler 对 Angular 指令进行单元测试
【发布时间】:2017-04-11 12:15:00
【问题描述】:

我尝试为我的示例指令编写单元测试,但如果我在 templateUrl 上替换 template,那么 element.triggerHangler 不会触发指令事件,我不明白为什么。

我的指令

'use strict';

angular.module('app')
  .directive('chatEditor', function() {
    return {
      scope: {
        onAdd: '&'
      },
      templateUrl: 'chat/chat-editor.directive.html',
      /*template: '<div></div>'*/
      link: function(scope, element, attrs) {
        element.on('click', function() {
          scope.onAdd({message: 'test'});
      });
    }
  }
});

单元测试

'use strict';

describe('chatEditor', () => {
  beforeEach(module('app', 'templates'));

  it('should add player', inject(($compile, $rootScope) => {
    $rootScope.onAddSpy = jasmine.createSpy('onAddSpy');
    const element = $compile('<chat-editor on-add="onAddSpy(message)"></chat-editor>')($rootScope);

    element.triggerHandler('click');

    $rootScope.$digest();

    expect($rootScope.onAddSpy).toHaveBeenCalledWith('test');
  }));
});

出错了

预计 spy onAddSpy 已使用 [ 'test' ] 调用,但从未调用过。

但如果我注释掉 templateUrl 并取消注释 template 则测试成功通过。

我还安装并配置了karma-ng-html2js-preprocessor。我来自karma.conf.js的剪辑

...

// list of files / patterns to load in the browser
files: [
  'app/**/*.module.js',
  'app/**/*.js',
  'app/**/*.html'
],

preprocessors: {
  'app/**/*.html': ['ng-html2js']
},

ngHtml2JsPreprocessor: {
  stripPrefix: 'app/',
  //prependPrefix: 'app/'
  moduleName: 'templates'
},
...

但这并不能解决问题。

【问题讨论】:

  • 我要检查的第一件事是模板是否真的被html2js 拾取。为此,请将$templateCache 注入您的测试,然后查看$templateCache.get('chat/chat-editor.directive.html') 返回的内容
  • @crizzis 是的,我检查了$templateCache.get('chat/chat-editor.directive.html'),我的模板成功返回。
  • @crizzis 如果我从浏览器手动测试我的指令,那么一切都按预期工作。
  • 你的模板内容是&lt;div&gt;&lt;/div&gt;吗?您在哪个浏览器中启动业力?一些浏览器(尤其是 PhantomJS)无法在单元测试中正确处理 triggerHandler,但这并不能解释为什么内联模板有效
  • @crizzis 是的,我试图将内容放在&lt;div&gt;&lt;/div&gt; 之间,但这并不能解决问题。我在 chrome 浏览器中启动业力。如果我手动将模板放入缓存$templateCache.put('chat/chat-editor.directive.html', '&lt;div&gt;&lt;/div&gt;'),那么问题仍然存在,即不太可能是ngHtml2JsPreprocessor 的问题。

标签: angularjs unit-testing karma-runner


【解决方案1】:

很久之后我才明白是什么问题。因为templateUrl是异步的,所以在单元测试期间模板不能同步加载,分别在编译阶段我们得到&lt;chat-editor on-add="onAddSpy(message)"&gt;&lt;/chat-editor&gt;元素而不是&lt;chat-editor on-add="onAddSpy(message)"&gt;&lt;div&gt;&lt;/div&gt;&lt;/chat-editor&gt;

为了解决问题可以使用几种方法。

1) 将编译阶段移至 beforeEach 块

'use strict';

describe('chatEditor', () => {
  let element;

  beforeEach(module('app', 'templates'));

  beforeEach(inject(($compile, $rootScope) => {
    element = $compile('<chat-editor on-add="onAddSpy(message)"></chat-editor>')($rootScope);
    $rootScope.$digest();
  }));

  it('should add player', inject($rootScope => {
    $rootScope.onAddSpy = jasmine.createSpy('onAddSpy');

    element.triggerHandler('click');
    expect($rootScope.onAddSpy).toHaveBeenCalledWith('test');
  }));
});

2) 使用 $timeout

'use strict';

describe('chatEditor', () => {
  beforeEach(module('app', 'templates'));

  it('should add player', inject(($compile, $rootScope, $timeout) => {
    $rootScope.onAddSpy = jasmine.createSpy('onAddSpy');

    const element = $compile('<chat-editor on-add="onAddSpy(message)"></chat-editor>')($rootScope);
    $rootScope.$digest();

    $timeout(() => {
      element.triggerHandler('click');
      expect($rootScope.onAddSpy).toHaveBeenCalledWith('test');
    });

    $timeout.flush();
  }));
});

【讨论】:

  • 出于好奇,您是否尝试过不使用$timeout 的解决方案#2?在我看来,在triggerHandler 之前调用$rootScope.$digest() 应该已经解决了问题
  • @crizzis 是的,你是对的。在 $compile 之后立即调用 $rootScope.$digest 即可解决问题。我稍后会更新我的答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-08
  • 2020-02-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多