Skip to content

Comment tester un formulaire ngModel ?

Voici un composant:

ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-signup',
  template: `
    <form action="#">
        <div>
            <label for="email" class="label">Votre email</label>
            <input type="email" name="email" [(ngModel)]="email" id="email" class="input" placeholder="name@company.com" required="">
        </div>
        <button type="submit" class="button">Créer le compte</button>
    </form>
  `
})
export class SignupComponent {
  email: string = ''
}
import { Component } from '@angular/core';

@Component({
  selector: 'app-signup',
  template: `
    <form action="#">
        <div>
            <label for="email" class="label">Votre email</label>
            <input type="email" name="email" [(ngModel)]="email" id="email" class="input" placeholder="name@company.com" required="">
        </div>
        <button type="submit" class="button">Créer le compte</button>
    </form>
  `
})
export class SignupComponent {
  email: string = ''
}

Le test unitaire serait alors le suivant:

ts
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FormsModule } from '@angular/forms';
import { SignupComponent } from './signup.component';

describe('MonComposantComponent', () => {
    let component: SignupComponent;
    let fixture: ComponentFixture<SignupComponent>;

    beforeEach(async () => {
        await TestBed.configureTestingModule({
            declarations: [SignupComponent],
            imports: [FormsModule]
        }).compileComponents();
    });

    beforeEach(() => {
        fixture = TestBed.createComponent(SignupComponent);
        component = fixture.componentInstance;
        fixture.detectChanges();
    });

    // test la propriété email après avoir rempli le champ email
    it('should have email property', async () => {
        const emailInput = fixture.nativeElement.querySelector('input[type="email"]');
        emailInput.value = 'aa@aa.net'
        emailInput.dispatchEvent(new Event('input'));
        await fixture.whenStable()
        fixture.detectChanges()
        expect(component.email).toBe(emailInput.value)
    })
});
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FormsModule } from '@angular/forms';
import { SignupComponent } from './signup.component';

describe('MonComposantComponent', () => {
    let component: SignupComponent;
    let fixture: ComponentFixture<SignupComponent>;

    beforeEach(async () => {
        await TestBed.configureTestingModule({
            declarations: [SignupComponent],
            imports: [FormsModule]
        }).compileComponents();
    });

    beforeEach(() => {
        fixture = TestBed.createComponent(SignupComponent);
        component = fixture.componentInstance;
        fixture.detectChanges();
    });

    // test la propriété email après avoir rempli le champ email
    it('should have email property', async () => {
        const emailInput = fixture.nativeElement.querySelector('input[type="email"]');
        emailInput.value = 'aa@aa.net'
        emailInput.dispatchEvent(new Event('input'));
        await fixture.whenStable()
        fixture.detectChanges()
        expect(component.email).toBe(emailInput.value)
    })
});
  1. emailInput.dispatchEvent(new Event('input')) simule un événement "input" sur l'élément emailInput. Cela permet de déclencher toutes les mécaniques liées à la modification de la valeur d'un champ dans le formulaire, telles que la mise à jour de la propriété email liée à ce champ via [(ngModel)]="email".

  2. await fixture.whenStable() attend que tous les observables liés à l'application soient résolus avant de continuer à exécuter le reste du code. Cela garantit que toutes les mises à jour liées à la modification de la valeur du champ email ont été effectuées avant de tester la valeur de la propriété email.

TIP

ngModelest asynchrone

fixture.detectChanges() déclenche une nouvelle itération de détection de modifications dans l'application. Cela permet de prendre en compte les modifications apportées à la propriété email lors de la simulation de l'événement "input" sur l'élément emailInput.

Tester

Le composant:

ts
import { Component } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';

@Component({
  selector: 'app-signup',
  template: `
    <form [formGroup]="signupForm" (ngSubmit)="onSubmit()">
        <div>
            <label for="email" class="label">Votre email</label>
            <input type="email" name="email" formControlName="email" id="email" class="input" placeholder="name@company.com" required="">
        </div>
        <button type="submit" class="button">Créer le compte</button>
    </form>
  `
})
export class SignupComponent {
  signupForm = new FormGroup({
    email: new FormControl('', [Validators.required, Validators.email])
  });

  onSubmit() {
    console.log(this.signupForm.value);
  }
}
import { Component } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';

@Component({
  selector: 'app-signup',
  template: `
    <form [formGroup]="signupForm" (ngSubmit)="onSubmit()">
        <div>
            <label for="email" class="label">Votre email</label>
            <input type="email" name="email" formControlName="email" id="email" class="input" placeholder="name@company.com" required="">
        </div>
        <button type="submit" class="button">Créer le compte</button>
    </form>
  `
})
export class SignupComponent {
  signupForm = new FormGroup({
    email: new FormControl('', [Validators.required, Validators.email])
  });

  onSubmit() {
    console.log(this.signupForm.value);
  }
}

et le test unitaire:

ts
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { SignupComponent } from './signup.component';

describe('SignupComponent', () => {
    let component: SignupComponent;
    let fixture: ComponentFixture<SignupComponent>;

    beforeEach(async () => {
        await TestBed.configureTestingModule({
            declarations: [SignupComponent],
            imports: [FormsModule, ReactiveFormsModule]
        })
            .compileComponents();
    });

    beforeEach(() => {
        fixture = TestBed.createComponent(SignupComponent);
        component = fixture.componentInstance;
        fixture.detectChanges();
    });

    it('should create', () => {
        expect(component).toBeTruthy();
    });

    it('form should be invalid', () => {
        expect(component.signupForm.valid).toBeFalsy();
    });

    it('email field validity', () => {
        let email = component.signupForm.controls['email'];
        expect(email.valid).toBeFalsy();

        email.setValue("test");
        expect(email.hasError('email')).toBeTruthy();

        email.setValue("test@test.com");
        expect(email.valid).toBeTruthy();
    });
});
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { SignupComponent } from './signup.component';

describe('SignupComponent', () => {
    let component: SignupComponent;
    let fixture: ComponentFixture<SignupComponent>;

    beforeEach(async () => {
        await TestBed.configureTestingModule({
            declarations: [SignupComponent],
            imports: [FormsModule, ReactiveFormsModule]
        })
            .compileComponents();
    });

    beforeEach(() => {
        fixture = TestBed.createComponent(SignupComponent);
        component = fixture.componentInstance;
        fixture.detectChanges();
    });

    it('should create', () => {
        expect(component).toBeTruthy();
    });

    it('form should be invalid', () => {
        expect(component.signupForm.valid).toBeFalsy();
    });

    it('email field validity', () => {
        let email = component.signupForm.controls['email'];
        expect(email.valid).toBeFalsy();

        email.setValue("test");
        expect(email.hasError('email')).toBeTruthy();

        email.setValue("test@test.com");
        expect(email.valid).toBeTruthy();
    });
});