3

我正在装饰这样的表格:

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

4

3 回答 3

7

你有一些问题:

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

运行演示

于 2014-07-05T05:18:19.643 回答
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 指令,因为这更适合提交表单。我实际上有一个很好的例子,因为我在我开源的验证模块中做了这个。这应该会给你一个很好的开始。

指令来源在这里

指令测试在这里

于 2014-07-02T20:39:12.480 回答
0

尝试连接到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);
  });
于 2014-07-04T17:35:28.797 回答