|
Publié le par Chloé MAHALIN

Angular 4

Tester un composant HTML select dans un composant Angular

Prérequis, ajouter un select à une vue.

Objectif

Angular4 est un framework web qui n'exclue pas le test unitaire. Un code maintenable et durable passe par la mise en place de tests unitaires et d'intégration. D'autant plus que Karma et Jasmine.js, déjà intégrés au build angular-cli, permettent les deux pratiques (TDD, BDD).

Mais encore faut-il savoir gérer l'événementiel des composants HTML dans les TU avec Angular.

Ici, nous allons voir comment déclencher un événement depuis un select et comment vérifier le contenu d'un select.

Préparons notre test

Créons un composant simple :

myComponent.html

<div>
    <select [(ngModel)]="selected">
        <option [ngValue]="''"> -- Choisir -- </option>
        <option [ngValue]="'FRANCE'">France</option>
        <option [ngValue]="'CANADA'">Canada</option>
        <option [ngValue]="'MAROC'">Maroc</option>
    </select>
    {{selected}}
</div>

myComponent.ts

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

@Component({
  selector: 'myComponent-tag',
  templateUrl: './myComponent.html',
  styleUrls: ['./myComponent.css']
})
export class MyComponent {

  selected: string;
}

Je vous épargne la rédaction du css, puisque le contenu sera vide.

Pour résumer ! Nous disposons dans la vue d'un select dont la valeur est liée à la variable 'selected' du model de mon composant.

Lorsqu'une sélection aura lieu, le contenu de ma variable sera mis à jour. Cela devrait se constater immédiatement puisque l'on affiche le contenu de la variable selected directement dans la vue à travers la ligne :

{{selected}}

Créons la classe de test associée :

myComponent.spec.ts

import { ComponentFixture, TestBed, tick, fakeAsync, async } from '@angular/core/testing';
import { FormBuilder, FormsModule, ReactiveFormsModule  } from '@angular/forms';
import { DebugElement } from '@angular/core';
import { By } from '@angular/platform-browser';
import { MyComponent } from './myComponent';

describe('Input testing :', () => {

  let comp:                 MyComponent;
  let fixture:              ComponentFixture<MyComponent>;

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

  beforeEach(() => {
    fixture = TestBed.createComponent(MyComponent);
    comp = fixture.componentInstance;
  });

});

On peut constater que nous allons évaluer dans un environnement de test simulé asynchrone. Ceci afin de permettre les mises à jour depuis et vers la vue. Nous allons ajouter des tests à cette classe.

Vérifier la valeur contenue dans un select

myComponent.spec.ts

  it('the selected value should be updated with the variable value', fakeAsync(() => {
    // On met à jour la variable dans le modèle
    comp.selected = 'MAROC';
    // On détecte les modifications depuis le modèle.
    fixture.detectChanges();
    tick();

    // On récupère le select dans la vue
    const select = fixture.debugElement.query(By.css('select')).nativeElement;

    // On vérifie la valeur du select
    expect(select.value).toEqual('3: MAROC');
  }));

Pour faire ce test, nous avons du positionner notre contexte dans une fake Asynchrone zone par l'utilisation de la déclaration fakeAsync. Pour plus d'informations sur async, fakeAsync ou tick, voir l'article qui est consacré au sujet du test asynchrone.

Dans cet exemple, on met à jour la variable directement depuis le modèle, puis on vérifie que le contenu du champ de sélection a bien été mis à jour avec cette valeur. Faisons maintenant le test inverse :

Déclencher la mise à jour du modèle depuis le select

Maintenant, nous allons vérifier que le modèle se met à jour depuis la sélection dans la vue.

myComponent.spec.ts

  it('the variable should be updated with the selection', () => {
    // On récupère le select dans la vue
    const select = fixture.debugElement.query(By.css('select')).nativeElement;

    // On sélectionne dans la liste
    select.value = '2: CANADA';
    select.dispatchEvent(new Event('change'));

    // On vérifie la valeur de la variable
    expect(comp.selected).toEqual('CANADA');
  });

Nous avons pu tester que le model est mis à jour depuis la vue. Maintenant, supposons que je souhaite vérifier l'appel à une méthode depuis un événement sur le select.

Simuler un évènement sur un input

Reprenons notre composant et rajoutons-lui un peu de contenu :

myComponent.html

<div>
  <select [(ngModel)]="selected" (change)='checkSelectedIsNotEmpty()'>
    <option selected [ngValue]="''"> -- Choisir -- </option>
    <option [ngValue]="'FRANCE'">France</option>
    <option [ngValue]="'CANADA'">Canada</option>
    <option [ngValue]="'MAROC'">Maroc</option>
  </select>
  {{selected}} {{ selectedIsEmpty ? 'A selection should be done' : '' }}
</div>

myComponent.ts

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

@Component({
  selector: 'myComponent-tag',
  templateUrl: './myComponent.html',
  styleUrls: ['./myComponent.css']
})
export class MyComponent {

    selected: string = '';
    selectedIsEmpty: boolean = true;

    checkSelectedIsNotEmpty(): void {
        this.selectedIsEmpty = this.selected.length == 0;
    }
}

Nous avons rajouté une méthode qui se déclenchera à chaque fois d'une sélection aura lieu dans le select. Elle vérifie qu'une valeur a été selectionnée.

Maintenant, écrivons le test qui correspond. Logiquement, il s'agit de déclencher un évènement change depuis le select.

myComponent.spec.ts

it('the method should be triggered with the selection', () => {
    // On récupère le select dans la vue
    const select = fixture.debugElement.query(By.css('select')).nativeElement;

    // On sélectionne dans la liste
    select.value = '2: CANADA';
    select.dispatchEvent(new Event('change'));

    // On vérifie la valeur de la variable
    expect(comp.selectedIsEmpty).toBeFalsy();
});

En fait, le change est exactement le même événement que celui déclenché lors d'une sélection, donc la différence n'est pas flagrante. Mais l'idée est là. Libre à vous de déclencher les événements qui vous chantent sur votre select.