角度测试:如何期望触发元素事件?

时间:2014-06-25 00:48:11

标签: angularjs jasmine angular-directive

我正在装饰这样的表格:

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方法是什么?

3 个答案:

答案 0 :(得分:7)

你有一些问题:

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

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

答案 1 :(得分:1)

这可能与提出“提交”有关。测试期间的事件。

棱角分明的团队已经创建了一个非常时髦的课程来帮助他们做到这一点,它似乎涵盖了很多边缘案例 - 请参阅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。

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

指令来源为here

指令测试是here

答案 2 :(得分:0)

尝试连接模糊事件:

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