Blog ENI : Toute la veille numérique !
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
💥 Du 22 au 24 novembre : Accès 100% GRATUIT
à la Bibliothèque Numérique ENI. Je m'inscris !
  1. Livres et vidéos
  2. JavaScript et Angular
  3. Tests unitaires
Extrait - JavaScript et Angular Des bases du langage au développement d'une application web
Extraits du livre
JavaScript et Angular Des bases du langage au développement d'une application web
2 avis
Revenir à la page d'achat du livre

Tests unitaires

Commandes Git

Les commandes GIT suivantes permettent d’accéder au code source des chapitres précédents :

cd C:\ 
git clone https://github.com/EditionsENI/JavaScriptEtAngular.git 
cd C:\JavaScriptEtAngular\Angular 
git checkout chapitre17 
code . 

Introduction

Les tests font partie intégrante du cycle de développement d’une application. Dans un processus d’intégration continue, si un test ne se déroule pas correctement, c’est l’ensemble du processus qui est stoppé.

En règle générale, tester une application, c’est vérifier qu’à partir de données connues, son comportement correspond parfaitement à ce qui était attendu. Par exemple, tester une calculatrice, c’est prendre deux nombres, les additionner et observer le résultat. Si celui-ci est le même que celui attendu, alors le test est concluant.

Angular permet l’écriture de tests rapidement et simplement. Jasmine et Karma sont les deux librairies en charge des tests unitaires (et Protractor des tests de bout en bout - cf. chapitre Tests de bout en bout).

Un test unitaire est un test permettant de vérifier une portion d’un élément isolé. Ce peut être les méthodes d’un composant, d’un service, d’un tube et même d’une balise HTML.

Un test unitaire ne vérifie pas les dépendances. Il va donc falloir simuler le résultat d’appel des dépendances afin de ne pas biaiser le test. La modification d’un service, par exemple, ne doit jamais faire échouer les tests unitaires d’un composant qui en dépend....

Jasmine

Par convention, Angular recherche les tests unitaires dans les fichiers se terminant par spec.ts. Le fichier de tests unitaires de la classe TypeScript maClasse.ts est maClasse.spec.ts. Lors de la génération d’un nouvel élément avec Angular CLI, un fichier de tests unitaires est toujours créé.

ng generate class classes/C17calculatrice 

Jasmine possède plusieurs méthodes indispensables pour une bonne écriture de tests. Le minimum est :

  • describe() : pour déclarer un groupe de plusieurs tests.

  • it() : pour déclarer un seul test.

  • expect() : pour définir le comportement attendu.

describe(description, ( ) { 
        it(nomDuTest, ( ) { 
                expect().methodeJasmine() ; 
        } 
}); 

methodeJasmine() est une méthode de la librairie Jasmine qui introduit le concept de paramètre de comparaison. Les paramètres de comparaison sont suffisamment explicites pour peu que le développeur soit un minimum anglophone. 

  • toBe : s’attend à trouver une égalité stricte (égalité de valeurs et de types).

  • toEqual : s’attend à trouver une égalité non stricte (égalité de valeurs).

  • toContain : s’attend à trouver un élément à l’intérieur d’un tableau ou d’une chaîne de caractères.

  • toBeDefined : s’attend à trouver un objet défini.

  • toBeNull : s’attend à trouver une valeur de l’objet nulle.

  • toBeTruthy : s’attend à trouver une valeur de l’objet vraie.

  • toBeFalsy : s’attend à trouver une valeur de l’objet...

Tests unitaires d’un composant Angular

1. Classe TypeScript

Afin de tester un composant Angular, une librairie appelée @angular/core/testing est utilisée. Dans cette librairie, la classe TestBed va permettre de définir un environnement approprié pour le test du composant.

La méthode TestBed.configureTestingModule( { } ) est comparable dans sa syntaxe au décorateur @NgModule() du fichier app.module.ts. Elle prend en paramètre un objet composé des attributs imports, declarations, providers et schemas.

TestBed.configureTestingModule( { 
        imports: [ ], 
        declarations: [ ], 
        providers: [ ], 
        schemas: [ ] 
}).compileComponents(); 

Le module de test va ainsi contenir tous les objets nécessaires au composant à tester.

ng generate component composants/C17 

La première chose à tester est de savoir si le composant a réussi à s’instancier.

TestBed permet la création d’un composant grâce à la méthode createComponent(unComposant).

maVariable = TestBed.createComponent(monComposant); 

Le test portera sur l’instance de la variable maVariable. Il suffit de vérifier si elle est à vrai.

Puisque l’ensemble des tests portera sur le composant créé, il est très fortement recommandé de mettre toutes les opérations de création du composant dans la méthode beforeEach().

// C:\JavaScriptEtAngular\Angular\monProjetAngular\src\app\ 
composants\c17.component.spec.ts 
import {ComponentFixture, TestBed} from '@angular/core/testing'; 
import {C17Component} from './c17.component'; 
 
describe('Le composant C17', () => { 
 let composant: C17Component; 
 let fixture: ComponentFixture<C17Component>; 
 
 beforeEach(() => { 
     // Le composant est configuré et compilé 
     TestBed.configureTestingModule({ 
       imports: [ ], 
       declarations: [C17Component], 
       providers: [ ], 
       schemas: [ ] 
     }).compileComponents(); 
     // Le composant est créé 
     fixture = TestBed.createComponent(C17Component); 
     // L'instance du composant est dans une variable 
     composant = fixture.componentInstance; 
   } 
 ); 
 fit('doit être créé', () => { 
   // La variable est à vrai si le composant a bien été crée 
   expect(composant).toBeTruthy(); 
 }); 
}); 
images/17RI03.png

Exécution d’un test sur un composant

2. Gabarit

Tester les méthodes et l’instanciation d’un composant d’une classe TypeScript, c’est bien, mais on ne teste que la moitié du composant. Pour rappel, un composant est une classe TypeScript, un gabarit HTML et une feuille de style.

@angular/core/testing possède une interface debugElement qui permet d’interagir avec l’ensemble des éléments HTML du gabarit. Celle-ci peut récupérer un élément par son id, sa classe CSS ou sa balise.

Pour récupérer un élément HTML par sa balise :

unElementHTML = fixture.nativeElement.querySelector('balise'); 

Pour récupérer un élément HTML par sa classe CSS :

unElementHTML = fixture.nativeElement.querySelector('.css'); 

Pour récupérer un élément HTML par son id :

unElementHTML = fixture.nativeElement.querySelector('#id'); 

Pour interagir avec le gabarit, il faut rajouter :

  • une instruction pour détecter les changements dans le DOM

  • un objet DebugElement

Supposons un gabarit HTML :

<!-- C:\JavaScriptEtAngular\Angular\monProjetAngular\src\app\ 
composants\c17\c17.component.html --> 
<div> 
 <p>Un paragraphe</p> 
 <span id="uneSpan">Une span avec un id</span> 
 <span class="uneClasseCSS">Une span avec du CSS</span> 
 <span class="uneClasseCSS">Une autre</span> 
</div> 

Le tester en utilisant les trois récupérations possibles serait :

// C:\JavaScriptEtAngular\Angular\monProjetAngular\src\app\ 
composants\c17\c17.comonent.spec.ts 
import { ComponentFixture, TestBed } from '@angular/core/testing'; 
 
import { C17Component } from './c17.component'; 
import { DebugElement } from '@angular/core'; 
 
describe('Le composant C17 : Lapis', () => { 
 let component: C17Component; 
 let fixture: ComponentFixture<C17Component>; 
 let debugElement: DebugElement; 
 
 beforeEach(() => { 
   TestBed.configureTestingModule({ 
     imports: [], 
     declarations: [C17Component], 
     providers: [], 
     schemas: [] 
   }).compileComponents(); ...

Karma

Karma est un pur produit des équipes d’Angular. Couplé à Jasmine, il permet de tester le même code sur plusieurs navigateurs web différents de manière complètement automatisée.

Configuré par défaut pour fonctionner avec Jasmine lors de la création de l’application par Angular CLI, Karma laisse la possibilité au développeur de changer la librairie de test si Jasmine ne convient pas. De plus, Karma s’inscrit parfaitement dans un cycle de développement d’applications en intégration continue.

Le fichier de configuration de Karma se situe à la racine d’un projet Angular : karma.conf.js.

Il contient, entre autres, les plug-ins utilisés, les paramètres pour la couverture de code et l’ensemble des paramètres nécessaires au lancement d’un serveur et navigateur web.

Karma détecte chaque modification du code pendant qu’il est lancé et se recharge afin de prendre en compte les éléments les plus récents (autowatch).

Il peut lancer un ou plusieurs navigateurs web en même temps. Les valeurs possibles sont Chrome, Firefox, Opera, IE, Safari… Mais des plug-ins supplémentaires sont à ajouter.

npm install karma-firefox-launcher -save 
npm install karma-opera-launcher -save 
npm install karma-chrome-launcher -save 
npm install karma-ie-launcher...

Couverture de code

ng test possède une option particulièrement demandée par les chefs de projets. Celle-ci permet de déterminer le pourcentage de code couvert par les tests unitaires.

ng test --code-coverage --watch=false 

Cette commande produit deux éléments :

  • un tableau dans la console qui a lancé la commande

  • un site web visible dans un navigateur

images/17RI06.png

Visualisation de la couverture de code sur la console

Le site web est généré automatiquement dans un répertoire coverage à la racine du projet. Pour y accéder, il suffit d’ouvrir le fichier index.html dans un navigateur.

images/17RI07.png

Visualisation de la couverture de code dans un navigateur web

La couverture de code ne s’applique qu’au bout de code réellement exécuté. Ce qui signifie qu’un taux de couverture de 100 % peut être facilement obtenu en ne testant absolument rien. En effet, si app.component.ts n’est pas testé, le composant principal de l’application ne l’est pas et donc absolument rien n’est exécuté. Si rien n’est exécuté, alors tout est testé. Logique.

Dans une application classique, avec les fichiers de tests unitaires générés par Angular CLI, le rapport de couverture de code ressemble plus à cela :

images/17RI08.png

Couverture de code d’une application Angular

Le site web permet la navigation par liens hypertextes....

Mise en pratique

1. Énoncé

À partir de l’application PokemonManager développée dans les chapitres précédents, instaurez un minimum de couverture de code de 80 % sur les quatre éléments du rapport.

Faites en sorte d’atteindre une couverture de code de 80 %.

2. Correction

Pour l’instant, la commande ng test --code-coverage --watch=false ne remplit pas toutes les conditions de validité.

images/17RI11.png

Couverture de code initiale

Bien entendu, ces chiffres peuvent être différents pour vous.

Le service pokemons.service.ts est la classe la plus facile à tester puisqu’il suffit de vérifier que les trois méthodes sont bien implémentées. Il est inutile de vérifier le retour de l’appel asynchrone vers l’API puisqu’un test unitaire ne doit pas dépendre d’une autre classe. Ici, le test doit être concluant, que l’API fonctionne ou non.

// C:\JavaScriptEtAngular\Angular\PokemonManager\src\app\ 
services\pokemons.service.spec.ts  
import { TestBed, inject } from '@angular/core/testing'; 
 
import { PokemonsService } from './pokemons.service'; 
import { HttpClientTestingModule } from '@angular/common/ 
http/testing'; 
 
describe('PokemonsService', () => { 
 let service: PokemonsService; 
 
 beforeEach(() => { 
   TestBed.configureTestingModule({ 
     imports: [HttpClientTestingModule] 
   }); 
   service = TestBed.get(PokemonsService); 
 }); 
 
 it('doit être créé', () => { 
   expect(service).toBeTruthy();  
 }); 
 
 it('doit implémenter trois méthodes', () => { 
   expect(service.getGenerations()).not.toBeUndefined(); 
   expect(service.getPokemons()).not.toBeUndefined(); 
   expect(service.getPokemon('test')).not.toBeUndefined();   }); 
}); 

Le formulaire de recherche est une simple classe TypeScript contenant un constructeur et un setter. Tester cette classe est relativement aisé.

Le testeur va utiliser le constructeur, utiliser...