So testen Sie eine benutzerdefinierte Validierungs-Winkelanweisung

76

Diese benutzerdefinierte Validierungsrichtlinie ist ein Beispiel, das auf der offiziellen Winkel-Site vorgestellt wird. http://docs.angularjs.org/guide/forms Überprüft, ob eine Texteingabe im Zahlenformat vorliegt oder nicht.

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

Um diesen Code zu testen, habe ich Folgendes geschrieben:

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

Dann bekomme ich diesen Fehler:

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

Ich habe überall Print-Anweisungen platziert, um zu sehen, was los ist, und es sieht so aus, als würde die Direktive niemals aufgerufen. Was ist ein geeigneter Weg, um eine einfache Anweisung wie diese zu testen?

ghiden
quelle
Vielen Dank, dass Sie sich die Zeit genommen haben, eine Antwort zurückzubringen! Nur zu Ihrer Information, Sie können Ihre Antwort extrahieren und als akzeptiert für spätere Suchende markieren - das ist hier akzeptabel ;-)
Sean Vieira
Vielen Dank für Ihre Tipps. Ich habe meine Antwort verschoben.
Ghiden

Antworten:

88

Die Tests der anderen Antwort sollten wie folgt geschrieben werden:

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

Beachten Sie, dass $scope.$digest()jetzt nach aufgerufen wird $setViewValue. Dies versetzt das Formular in einen "schmutzigen" Zustand, andernfalls würde es "makellos" bleiben, was wahrscheinlich nicht das ist, was Sie wollen.

Jrief
quelle
Vielen Dank, hat mir heute sehr geholfen! Aber ich verwende direkt das Scope-Modell und nicht $setViewValue(), ich weiß nicht, ob mir viele Fälle fehlen ...?
Florent Destremau
67

Ich habe es herausgefunden, indem ich den Angular-App-Code https://github.com/angular-app/angular-app gelesen habe. Dieses Video hilft auch http://youtu.be/ZhfUv0spHCY?t=31m17s

Zwei Fehler, die ich gemacht habe:

  • Binden Sie nicht direkt an den Bereich, wenn Sie ng-model ausführen
  • Verwenden Sie den Formular-Controller, um direkt zu bearbeiten, was für Anweisungen übergeben werden soll

Hier ist die aktualisierte Version. Die Direktive ist dieselbe, nur der Test, den ich geändert habe.

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
quelle
@ghiden Das hat mir wirklich geholfen. Haben Sie jedoch Erfahrung damit gemacht, mehrere Direktiven zu verketten und das zu testen? Zum Beispiel, wenn Sie eine Anweisung für Telefon oder E-Mail gemacht haben? Ich erhalte jedes Mal eine Reihe von Fehlern, wenn ich versucht habe, eine zusätzliche Anweisung hinzuzufügen. Gedanken?
Wissenschaftler
Mehrere Direktiven verketten? Ich denke, auch wenn Sie mehrere Anweisungen auf ein Element anwenden, möchten Sie sie dennoch separat testen, richtig? Wenn jeder richtig funktioniert und die Priorität richtig eingestellt ist, sollten sie funktionieren. Manchmal mache ich zuerst so etwas wie eine Modellvalidierung und fahre dann mit der Anweisung für visuelle Effekte mit sehr niedriger Priorität fort.
Ghiden
@ghiden Ich habe mein Problem gefunden, ich habe eine Formularvalidierungsanweisung getestet und das ng-Modell verwendet, und ich glaube, mein Hauptproblem war, dass ich auch den Namen als Anweisung sowie ein Attribut für das Eingabeelement verwendet habe. Ich bin jetzt alle gut und teste es! Vielen Dank für Ihr Beispiel. Wirklich sehr geholfen.
Wissenschaftler
1
Dies funktionierte nicht für mich, bis ich $scope.$digest()nach dem Anruf hinzufügte $setViewValue. Mache ich etwas Seltsames oder fehlt das in deinem Beispiel? EDIT: Entschuldigung, habe die Antwort unten nicht gesehen.
AndyPerlitch
Ich kann nie den zweiten Test bestehen lassen (es ist immer wahr). Ich benutze 1.3.12. Hat sich etwas geändert?
Mocksy
2

Ich teste meine benutzerdefinierten Anweisungen und suche im Objekt "$ error" den Namen der benutzerdefinierten Validierung. Beispiel:

  '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('[email protected]').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('[email protected]').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)
  }));
});

Ich hoffe ich kann dir helfen!

EricdRose Lotso
quelle