Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular2 how to unit test a custom validator directive?

Tags:

I wrote a very simple custom validator for an input field:

import { Directive } from '@angular/core'; import { AbstractControl, NG_VALIDATORS } from '@angular/forms';  function numberValidator(c: AbstractControl) {     if (!c.value) return null;     return new RegExp('^[1-9][0-9]{6,9}$').test(c.value) ? null : {         validateNumber: {             valid: false         }     } }  @Directive({     selector: '[number-validator]',     providers: [         { provide: NG_VALIDATORS, multi: true, useValue: numberValidator }     ] }) export class NumberValidator {} 

I would like to unit test this validator. I read Test an attribute directive on the Angular2 page, but there is no css or html that changes. How can I unit test this validator?

like image 941
Robin Dijkhof Avatar asked Oct 10 '16 11:10

Robin Dijkhof


People also ask

How do you write a unit test for directives?

Unit Test in Jasmine You use TestComponentBuilder as shown in the lined SO question/answer. Create a test component where the directive is used in the template and then get a reference to the directive from the created test component instance.

What is a custom validator?

The CustomValidator control is a separate control from the input control it validates, which allows you to control where the validation message is displayed. Validation controls always perform validation on the server.


1 Answers

If you want to do it the easy way (which I would do, since all the logic is in the validator function), is just to test the validator function. Just pass a control to it

expect(numberValidator(new FormControl('123456'))).toEqual({   'validateNumber': { 'valid': false } }); expect(numberValidator(new FormControl('123456789'))).toEqual(null); 

If you really want to test it when "being used", then it gets a little tedious. These are usually the steps I take

  1. Create dummy component to use the directive
  2. Set up the test bed configuration
  3. Create the component to test.
  4. Get the native input element and dispatch an invalid input event to it
  5. Get the injector that holds the NgForm
  6. Check the form for failure
  7. Put a valid input and check that it passes.

It's a lot compared to just testing the validator method. But here it is anyway ;-) Enjoy!

import { Component, Directive } from '@angular/core'; import { TestBed, async } from '@angular/core/testing'; import { dispatchEvent } from '@angular/platform-browser/testing/browser_util'; import { By } from '@angular/platform-browser'; import { FormsModule, NG_VALIDATORS, AbstractControl,          NgForm, FormControl } from '@angular/forms';  function numberValidator(c: AbstractControl) {   if (!c.value) return null;   return new RegExp('^[1-9][0-9]{6,9}$').test(c.value) ? null : {     validateNumber: {       valid: false     }   }; }  @Directive({   selector: '[number-validator]',   providers: [     { provide: NG_VALIDATORS, multi: true, useValue: numberValidator }   ] }) export class NumberValidator { }  @Component({   template: `     <form>       <input name="number" type="text" ngModel number-validator />     </form>   ` }) class TestComponent { }  describe('component: TestComponent', () => {   beforeEach(() => {     TestBed.configureTestingModule({       imports: [ FormsModule ],       declarations: [TestComponent, NumberValidator]     });   });    it('should validate (easy)', () => {     expect(numberValidator(new FormControl('123'))).toEqual({       'validateNumber': { 'valid': false }     });     expect(numberValidator(new FormControl('123456789'))).toEqual(null);   });    it('should validate (tedious)', async(() => {     let fixture = TestBed.createComponent(TestComponent);     let comp = fixture.componentInstance;     let debug = fixture.debugElement;     let input = debug.query(By.css('[name=number]'));      fixture.detectChanges();     fixture.whenStable().then(() => {       input.nativeElement.value = '123';       dispatchEvent(input.nativeElement, 'input');       fixture.detectChanges();        let form: NgForm = debug.children[0].injector.get(NgForm);       let control = form.control.get('number');        // just to show a few different ways we can check validity       expect(control.hasError('validateNumber')).toBe(true);       expect(control.valid).toBe(false);       expect(form.control.valid).toEqual(false);       expect(form.control.hasError('validateNumber', ['number'])).toEqual(true);        input.nativeElement.value = '123456789';       dispatchEvent(input.nativeElement, 'input');       fixture.detectChanges();        expect(form.control.valid).toEqual(true);     });   })); }); 
like image 96
Paul Samsotha Avatar answered Oct 10 '22 23:10

Paul Samsotha