测试自定义验证 angularjs 指令

这个自定义验证指令是在官方角度站点上提供的一个示例。 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.

我把打印声明到处都是,看看发生了什么事情,它看起来像指令从来没有调用。 测试这样一个简单指令的正确方法是什么?

38029 次浏览

I figured it out by reading angular-app code https://github.com/angular-app/angular-app This video also helps too http://youtu.be/ZhfUv0spHCY?t=31m17s

Two mistakes that I made:

  • Do not bind directly to the scope when you are doing ng-model
  • Use form controller to directly manipulate what to pass for directives

Here is the updated version. The directive is the same, only the test that I changed.

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

The other answer's tests should be written as:

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

Note that $scope.$digest() now is invoked after $setViewValue. This sets the form into “dirty” state, otherwise it would remain “pristine”, which probably is not what you want.

I test my custom directives searching in the object "$error" the name of the custom validation. Example:

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

I Hope i can help you!