测试自定义验证 angularjs 指令

Posted

技术标签:

【中文标题】测试自定义验证 angularjs 指令【英文标题】:To test a custom validation angularjs directive 【发布时间】:2013-02-19 14:42:45 【问题描述】:

这个自定义验证指令是官方 Angular 网站上的一个示例。 http://docs.angularjs.org/guide/forms 它检查文本输入是否为数字格式。

var INTEGER_REGEXP = /^\-?\d*$/;
app.directive('integer', function() 
  return 
    require: 'ngModel',
    link: function(scope, elm, attrs, ctrl) 
      ctrl.$parsers.unshift(function(viewValue) 
        if (INTEGER_REGEXP.test(viewValue)) 
          // it is valid
          ctrl.$setValidity('integer', true);
          return viewValue;
         else 
          // it is invalid, return undefined (no model update)
          ctrl.$setValidity('integer', false);
          return undefined;
        
      );
    
  ;
);

为了对这段代码进行单元测试,我写了这个:

describe('directives', function() 
  beforeEach(module('exampleDirective'));

  describe('integer', function() 
    it('should validate an integer', function() 
      inject(function($compile, $rootScope) 
        var element = angular.element(
          '<form name="form">' +
            '<input ng-model="someNum" name="someNum" integer>' +
          '</form>'
          );
        $compile(element)($rootScope);
        $rootScope.$digest();
        element.find('input').val(5);
        expect($rootScope.someNum).toEqual(5);
      );
    );
  );
);

然后我得到这个错误:

Expected undefined to equal 5.
Error: Expected undefined to equal 5.

我将打印语句放在各处以查看发生了什么,看起来该指令从未被调用过。 测试这样一个简单指令的正确方法是什么?

【问题讨论】:

感谢您抽出宝贵时间回复答案!仅供参考,您可以提取您的答案并将其标记为已接受的答案以供以后的搜索者使用——这在此处是可以接受的;-) 感谢您的提示。我移动了我的答案。 【参考方案1】:

我通过阅读 angular-app 代码 https://github.com/angular-app/angular-app 弄清楚了 这个视频也有帮助http://youtu.be/ZhfUv0spHCY?t=31m17s

我犯了两个错误:

在做 ng-model 时不要直接绑定到作用域 使用表单控制器直接操作为指令传递的内容

这是更新版本。指令是一样的,只是我改的测试。

describe('directives', function() 
  var $scope, form;
  beforeEach(module('exampleDirective'));
  beforeEach(inject(function($compile, $rootScope) 
    $scope = $rootScope;
    var element = angular.element(
      '<form name="form">' +
        '<input ng-model="model.somenum" name="somenum" integer />' +
      '</form>'
    );
    $scope.model =  somenum: null 
    $compile(element)($scope);
    $scope.$digest();
    form = $scope.form;
  ));

  describe('integer', function() 
    it('should pass with integer', function() 
      form.somenum.$setViewValue('3');
      expect($scope.model.somenum).toEqual('3');
      expect(form.somenum.$valid).toBe(true);
    );
    it('should not pass with string', function() 
      form.somenum.$setViewValue('a');
      expect($scope.model.somenum).toBeUndefined();
      expect(form.somenum.$valid).toBe(false);
    );
  );
);

【讨论】:

@ghiden 这确实对我有帮助,但是,您是否有过链接多个指令并对其进行测试的经验?就像您对电话或电子邮件做出指示一样?每当我尝试添加附加指令时,我都会遇到一堆错误。想法? 链接多个指令?我猜即使您将多个指令应用于一个元素,您仍然想分别测试它们,对吗?如果每个都正常工作并且优先级设置正确,我想他们应该工作。我有时会先做模型验证之类的事情,然后再进行优先级很低的视觉效果指令。 @ghiden 我发现了我的问题,我正在测试一个表单验证指令,并使用 ng-model,我相信我的专业是问题也使用 name 作为指令,以及一个属性输入元素。不过我现在一切都很好,并且正在测试它!非常感谢你的例子。真的帮了很多忙。 这对我不起作用,直到我在调用$setViewValue 后添加了$scope.$digest()。我是在做一些奇怪的事情还是你的例子中缺少这个?编辑:对不起,没有看到下面的答案。 我永远无法通过第二个测试(它总是正确的)我使用 1.3.12 有什么改变吗?【参考方案2】:

另一个答案的测试应该写成:

describe('directives', function() 
  var $scope, form;
  beforeEach(module('exampleDirective'));
  beforeEach(inject(function($compile, $rootScope) 
    $scope = $rootScope;
    var element = angular.element(
      '<form name="form">' +
      '<input ng-model="model.somenum" name="somenum" integer />' +
      '</form>'
    );
    $scope.model =  somenum: null 
    $compile(element)($scope);
    form = $scope.form;
  ));

  describe('integer', function() 
    it('should pass with integer', function() 
      form.somenum.$setViewValue('3');
      $scope.$digest();
      expect($scope.model.somenum).toEqual('3');
      expect(form.somenum.$valid).toBe(true);
    );
    it('should not pass with string', function() 
      form.somenum.$setViewValue('a');
      $scope.$digest();
      expect($scope.model.somenum).toBeUndefined();
      expect(form.somenum.$valid).toBe(false);
    );
  );
);

请注意,$scope.$digest() 现在在 $setViewValue 之后调用。这会将表单设置为“脏”状态,否则它将保持“原始”状态,这可能不是您想要的。

【讨论】:

非常感谢,今天帮了我很多忙!但是我直接用的是scope模型而不是$setViewValue(),不知道是不是漏掉了很多case...?【参考方案3】:

我测试我的自定义指令,在对象“$error”中搜索自定义验证的名称。示例:

  'use strict';

describe('Directive: validadorCorreo', function () 

  // load the directive's module
  beforeEach(module('sistemaRegistroProCivilApp'));

  var inputCorreo, formulario, elementoFormulario, scope, $compile;

  beforeEach(inject(function ($rootScope, _$compile_) 
    scope = $rootScope.$new();
    $compile = _$compile_;

    elementoFormulario = angular.element('<form name="formulario">' + 
      '<input type="text" name="correo" data-ng-model="correo" required data-validador-correo/>' + 
      '</form');
    scope.correo = '';
    elementoFormulario = $compile(elementoFormulario)(scope);
    scope.$digest();
    inputCorreo = elementoFormulario.find('input');
    formulario = scope.formulario;
    console.log(formulario.correo.$error);
  ));

  it('Deberia Validar si un correo ingresado en el input es correcto e incorrecto', inject(function ($compile) 

    inputCorreo.val('eric+@eric.com').triggerHandler('input');
    expect(formulario.correo.$error.email).toBe(true); //Here, the name of the custom validation appears in the $error object.
    console.log(formulario.correo.$error);

    inputCorreo.val('eric@eric.com').triggerHandler('input');
    expect(formulario.correo.$error.email).toBeUndefined();//Here, the name of the custom validation disappears in the $error object. Is Undefined
    console.log(formulario.correo.$error.email)
  ));
);

希望能帮到你!

【讨论】:

以上是关于测试自定义验证 angularjs 指令的主要内容,如果未能解决你的问题,请参考以下文章

AngularJS 自定义表单验证指令在我的模式中不起作用

AngularJS自定义验证指令 - 如何避免使用隔离范围

angularjs 表单验证ngMessages和创建自定义指令结合

使用 ngMessages 验证自定义指令

AngularJS 自定义指令详解

AngularJS:directive自定义的指令