【问题标题】:Angular tests: How to expect element events to be triggered?Angular 测试:如何期望元素事件被触发?
【发布时间】:2014-08-15 10:04:09
【问题描述】:

我正在装饰这样的表格:

angular.module('Validation').directive('form', function() {
  return {
    restrict: 'E',
    link: function(scope, element) {
      var inputs = element[0].querySelectorAll('[name]');

      element.on('submit', function() {
        for (var i = 0; i < inputs.length; i++) {
          angular.element(inputs[i]).triggerHandler('blur');
        }
      });
    }
  };
});

现在,我正在尝试测试这个指令:

describe('Directive: form', function() {
  beforeEach(module('Validation'));

  var $rootScope, $compile, scope, form, input, textarea;

  function compileElement(elementHtml) {
    scope = $rootScope.$new();
    form = angular.element(elementHtml);
    input = form.find('input');
    textarea = form.find('textarea');
    $compile(form)(scope);
    scope.$digest();
  }

  beforeEach(inject(function(_$rootScope_, _$compile_) {
    $rootScope = _$rootScope_;
    $compile = _$compile_;

    compileElement('<form><input type="text" name="email"><textarea name="message"></textarea></form>');
  }));

  it('should trigger "blur" on all inputs when submitted', function() {
    spyOn(input, 'trigger');
    form.triggerHandler('submit');
    expect(input.trigger).toHaveBeenCalled(); // Expected spy trigger to have been called.
  });
});

但是,测试失败了。

测试该指令的正确 Angular 方法是什么?

【问题讨论】:

  • 您收到的错误信息是什么?
  • 您是在 phantomJS 还是其他无头浏览器中进行测试?

标签: angularjs jasmine angular-directive


【解决方案1】:

这可能与在测试期间引发“提交”事件有关。

Angular 团队创建了一个非常时髦的类来帮助他们做到这一点,它似乎涵盖了很多边缘情况 - 请参阅 https://github.com/angular/angular.js/blob/master/src/ngScenario/browserTrigger.js

虽然这个助手来自 ngScenario,但我在单元测试中使用它来克服在 PhantomJS 等无头浏览器中引发某些事件的问题。

我必须使用它来测试一个非常相似的指令,该指令在提交表单时执行操作,请参见此处的测试https://github.com/jonsamwell/angular-auto-validate/blob/master/tests/config/ngSubmitDecorator.spec.js(参见第 38 行)。

我必须使用它,因为我正在使用无头浏览器进行开发测试。似乎要在这种类型的浏览器中触发事件,触发事件的元素也必须附加到 dom。

此外,由于表单指令是 Angular 已有的指令,您应该装饰该指令或给该指令一个新名称。我实际上建议您装饰 ngSubmit 指令而不是 form 指令,因为这更适合提交表单。我实际上有一个很好的例子,因为我在我开源的验证模块中做了这个。这应该会给你一个很好的开始。

指令来源是here

指令测试是here

【讨论】:

  • “由于 form 指令是 Angular 已有的指令,你应该装饰这个指令或者给这个指令一个新名称。” 为什么?
  • @ExpertSystem 我相信它会与 angular form 指令冲突,如果使用具有明确含义的指令(即 blur-contorls-on-submit="" 代替),它看起来不像巫毒代码它只是神奇地发生在每一种形式上。一个应用程序应该由有意义的部分组成,仅仅因为我们在 html 领域并不意味着我们应该遵循指令的单一责任原则(无论如何我的 10 美分!!!!:-))
  • 我不会冲突。可以有尽可能多的同名指令(并且所有指令都应用于​​元素)。这真的取决于用户想要什么。如果您希望将某些内容应用于所有表单,那么声明另一个 form 指令而不是必须在每个表单上添加额外的指令是有意义的。
  • 我不同意将其应用于表单指令。不能期望在团队中工作的人知道每一段代码,如果在提交表单时出现奇怪的事情,他们将花费时间和精力来找出原因,甚至可能导致代码被破解以使其停止这样做。我认为指令应该简短而清晰,并定义它们对元素的作用以避免混淆。
  • 你错过了这里的重点。再次重申:“如果您希望将某些内容应用于 所有 表单,那么声明另一个表单指令是有意义的” 这是一个项目级别的决定。如果您需要将某些内容应用于所有表单,那么创建具有不同名称的指令不会自动将其应用于每个人的表单。无论如何,您需要通知所有其他成员。
【解决方案2】:

你有一些问题:

1) input = form.find('input');angular.element(inputs[i]); 是 2 个不同的包装器对象,它们包装了相同的底层 DOM 对象。

2) 你应该在triggerHandler 上创建一个间谍。

3) 您直接使用难以进行单元测试的 DOM。

这方面的一个例子是:angular.element(inputs[i]) 没有注入,所以我们很难在我们的单元测试中伪造它。

确保1)点返回相同的对象。我们可以伪造angular.element 以返回一个预训练 值,即input = form.find('input');

//Jasmine 1.3: andCallFake
//Jasmine 2.0: and.callFake
angular.element = jasmine.createSpy("angular.element").and.callFake(function(){
         return input; //return the same object created by form.find('input');
});

旁注:由于form 已经是一个angularJs 指令,为避免与已定义的指令冲突,您应该创建另一个指令并将其应用于form。像这样的:

<form mycustomdirective></form>

我不确定这是否有必要。因为我们正在监视一个可能在许多地方使用的全局函数(angular.element),所以我们可能需要保存以前的函数并在测试结束时恢复它。您的完整源代码如下所示:

it('should trigger "blur" on all inputs when submitted', function() {

    var angularElement = angular.element; //save previous function

    angular.element = jasmine.createSpy("angular.element").and.callFake(function(){
         return input;
    });

    spyOn(input, 'triggerHandler');
    form.triggerHandler('submit');

    angular.element = angularElement; //restore
    expect(input.triggerHandler).toHaveBeenCalled(); // Expected spy trigger to have been called.
  });

Running DEMO

【讨论】:

    【解决方案3】:

    尝试加入 blur 事件:

      it('should trigger "blur" on all inputs when submitted', function() {
        var blurCalled = false;
        input.on('blur', function() { blurCalled = true; });
        form.triggerHandler('submit');
        expect(blurCalled).toBe(true);
      });
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-28
      • 2015-04-17
      • 2021-01-02
      相关资源
      最近更新 更多