Publié le 08/21/2017, rédigé par Chloé MAHALIN

Angular 4

Tester un composant HTML input dans un composant Angular

Prérequis
- [ajouter un input à une vue](/university/frameworks/angular/107_add_input_component_in_view).

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

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

Ici

Préparons notre test

Créons un composant simple :

myComponent.html

<div>
    <input type="text" maxlength="20" placeholder="Type your text here" [(ngModel)]="text" />
    {{text}}
</div>

myComponent.ts

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

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

  text: string;
}

Je vous épargne la rédaction du css

Pour résumer ! Nous disposons dans la vue d'un input dont la valeur est liée à la variable 'text' du model de mon composant. Lorsque l'input sera mis à jour

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 input

C'est le plus simple.

myComponent.spec.ts

  it('the input value should be updated with the variable value'
    - () => {
    // On met à jour la variable dans le modèle
    comp.text = 'ceci est un test';
    // On détecte les modifications depuis le modèle.
    fixture.detectChanges();

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

    // On vérifie la valeur de l'input
    expect(input.value).toEqual('ceci est un test');
  });

Dans cet exemple

Faisons maintenant le test inverse :

Déclencher la mise à jour du modèle depuis l'input

Maintenant

myComponent.spec.ts

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

    // On rajoute du contenu dans l'input
    input.value = 'ceci est un autre test';
    input.dispatchEvent(new Event('input'));
    tick();
    fixture.detectChanges();

    // On vérifie la valeur de la variable
    expect(comp.text).toEqual('ceci est un autre test');
  }));

Pour faire ce test

Nous avons pu tester que le model est mis à jour depuis la vue. Maintenant

Simuler un évènement sur un input

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

myComponent.html

<div>
    <input type="text" maxlength="20" placeholder="Type your text here" [(ngModel)]="text" (keyup)="checkInputIsEmpty()" />
    {{text}} {{ textIsEmpty ? ' is empty' : ' is not empty' }}
</div>

myComponent.ts

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

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

  text: string = '';
  textIsEmpty: boolean = true;

  checkInputIsEmpty(): void {
    this.textIsEmpty = this.text.length == 0;
  }
}

Nous avons rajouté une méthode qui se déclenchera à chaque fois d'une touche de clavier est actionnée au moment ou la touche remonte (fin du contact touche). Elle vérifie que le contenu de l'input est vide ou non.

Maintenant

myComponent.spec.ts

  it('the method should be triggered with the keyup event on the input'
    - fakeAsync(() => {
    // spyOn(comp
    - 'checkInputIsEmpty'); <--- you can also spy on the method to check the triggering.

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

    // On rajoute du contenu dans l'input
    input.value = 'triggered';
    input.dispatchEvent(new Event('input'));
    // Voici l'évènement keyup qui va déclencher l'appel à la méthode.
    input.dispatchEvent(new Event('keyup'));
    tick();
    fixture.detectChanges();

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