Live le jeudi 30 octobre à 19h - Interview avec Gérôme Grignon

Développeur front-end et figure de la communauté Angular

Découvrez le parcours de Gérôme Grignon, développeur front-end, contributeur open source et membre actif de la communauté Angular en France.

Dans cette interview, il partage son cheminement personnel et professionnel : de sa découverte du front-end à son engagement dans la pédagogie et la transmission.

Skip to content

Vous souhaitez recevoir de l'aide sur ce sujet ? rejoignez la communauté Angular.fr sur Discord.

Comment tester un formulaire ngModel ?

Voici un composant:

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

@Component({
  selector: 'app-signup',
  imports: [FormsModule],
  standalone: true,
  template: `
    <form action="#">
        <div>
            <label for="email" class="label">Votre email</label>
            <input type="email" name="email" [(ngModel)]="email" id="email" class="input" placeholder="[email protected]" 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({
            imports: [SignupComponent, 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 = '[email protected]'
        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 { ReactiveFormsModule, FormControl, FormGroup, Validators } from '@angular/forms';

@Component({
  selector: 'app-signup',
  standalone: true,
  imports: [ReactiveFormsModule],
  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="[email protected]" 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({
            imports: [SignupComponent, 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("[email protected]");
        expect(email.valid).toBeTruthy();
    });
});