
Guide Angular testing : le mocking de service avec Jasmine
Dans le cadre des tests unitaires, le mocking est une technique essentielle pour simuler le comportement des unités de code et tester efficacement notre projet. Dans le contexte d’Angular, le mocking de services est particulièrement utile pour tester efficacement nos composants Angular en isolant les dépendances et en simulant leur comportement.
Bien que de nombreux développeurs préfèrent les tests d’intégration, ils négligent souvent les avantages des tests unitaires, tels que la rapidité, la fiabilité et le contrôle de la couverture du code. Il est donc important de maîtriser le mocking de services Angular pour tester, au minimum, le code critique de nos composants.
Dans ce guide, nous allons explorer comment mocker correctement les services dans Angular, en appliquant les meilleures pratiques et les outils disponibles. Vous apprendrez à configurer votre environnement de test, à créer des mocks efficaces et à écrire des tests unitaires robustes pour vos applications Angular.
Pourquoi mocker un service dans Angular ?
Les services que nous développons et autres providers (Router
, HttpClient
, etc.) sont souvent des éléments clés de nos applications Angular. Ils encapsulent la logique métier, les appels HTTP, permettent une communication transversale entre les composants et gèrent l’état de l’application.
Lorsque nous testons nos composants Angular, nous voulons nous concentrer sur le comportement du composant lui-même, sans nous soucier de celui des services qu’il utilise. Grâce au mocking des services, nous pouvons contrôler les dépendances en simulant tout ou partie du comportement des providers utilisés par le composant, ce qui nous permet de tester des scénarios spécifiques et de garantir le bon fonctionnement du composant.
Mocker un service Angular avec Jasmine
Jasmine est le framework de test par défaut pour les applications Angular. Il offre des fonctionnalités puissantes pour écrire des tests unitaires et des mocks.
Pour mocker un service ou un provider Angular avec Jasmine, il est possible d’utiliser deux méthodes :
- Une méthode simple et fonctionnelle qui consiste à créer un objet mock manuellement avec Jasmine
- Une méthode avancée qui bouchonne uniquement les méthodes nécessaires à l’exécution du test
Méthode simple : créer un mock manuellement
Nous retrouvons souvent cette méthodes sur les forums et les tutoriels. Elle consiste à créer un objet mock manuellement en utilisant Jasmine. Ce mock est ensuite injecté dans le composant grâce à la méthode TestBed.configureTestingModule
.
Pour tester le composant FirstComponent
qui utilise le service DataService
, la méthode s’applique de la manière suivante :
// /src/app/first/first.component.spec.ts
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FirstComponent } from './first.component';
+import { DataService } from '../data.service';
describe('FirstComponent', () => {
let component: FirstComponent;
let fixture: ComponentFixture<FirstComponent>;
+ let service: DataService;
+
beforeEach(async () => {
+ service = jasmine.createSpyObj(
+ 'DataService',
+ [
+ 'addRandomNumber',
+ 'removeNumber',
+ ],
+ ); # <1>
+
await TestBed.configureTestingModule({
- imports: [FirstComponent]
+ imports: [FirstComponent],
+ providers: [
+ { provide: DataService, useValue: service },
+ ],
})
.compileComponents();
// ...
it('should create', () => {
expect(component).toBeTruthy();
});
+
+ it('should add item', () => {
+ component.onAdd();
+ expect(service.addRandomNumber).toHaveBeenCalled();
+ });
+
+ it('should remove item', () => {
+ component.onRemove(0);
+ expect(service.removeNumber).toHaveBeenCalledWith(0);
+ });
});
Mocking du service DataService
avec Jasmine. Les méthodes addRandomNumber
et removeNumber
sont bouchonnées pour les tests.
Méthode avancée : bouchonner des méthodes précises
Cette seconde méthode est plus précise et permet de bouchonner uniquement les services ou les providers nécessaires à la validation d’un scénario de test. Dans ce cas, nous laissons le TestBed
initialiser le composant avec les services réels, puis nous bouchonnons spécifiquement leurs méthodes.
Le mocking de service est possible grâce à la fonction inject
du module Angular @angular/core/testing
et la fonction spyOn
de Jasmine. Pour le même composant FirstComponent
et le service DataService
, la méthode avancée s’applique de la manière suivante :
// /src/app/first/first.component.spec.ts
-import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { ComponentFixture, inject, TestBed } from '@angular/core/testing';
import { FirstComponent } from './first.component';
+import { DataService } from '../data.service';
describe('FirstComponent', () => {
let component: FirstComponent;
// ...
it('should create', () => {
expect(component).toBeTruthy();
});
+
+ it('should add item', inject([DataService], (service: DataService) => {
+ spyOn(service, 'addRandomNumber'); // <1>
+ component.onAdd();
+ expect(service.addRandomNumber).toHaveBeenCalled();
+ }));
+
+ it('should remove item', inject([DataService], (service: DataService) => {
+ spyOn(service, 'removeNumber'); // <1>
+ component.onRemove(0);
+ expect(service.removeNumber).toHaveBeenCalledWith(0);
+ }));
});
Seule la méthode spécifique du service DataService
est bouchonnée pour le scénario de test.
La meilleure méthode pour le mocking de service
Rappel sur les tests unitaires
Les tests unitaires consistent à vérifier le bon fonctionnement d’une unité de code (fonction, méthode, classe, etc.) dans des conditions permettant de valider son comportement de manière exhaustive.
Cependant, il n’est pas toujours nécessaire d’isoler complètement l’unité de code pour la tester efficacement. Comme je le mentionne dans mon livre Apprendre à programmer ses premières applications, l’objectif est d’écrire le moins de tests possibles tout en maximisant le taux de couverture du code source. Cela garantit des tests unitaires efficaces et maintenables.
Avantages et inconvénients des deux méthodes
Le tableau suivant résume les avantages et les inconvénients des deux méthodes de mocking de service Angular :
Méthode | Avantages | Inconvénients |
Simple |
|
|
Avancée |
|
|
Privilégier la méthode selon le contexte
Comme toujours, rien n’est complètement blanc ou noir. Selon le contexte, la complexité de l’unité de code et la nature des tests, il est important de choisir la méthode de mocking la plus adaptée.
Je tends à privilégier la méthode avancée car elle permet de tester rigoureusement le comportement du code source sans passer trop de temps à écrire les tests.
Dans l’exemple précédent, le mocking du service DataService
n’était pas nécessaire pour valider le comportement du composant FirstComponent
. Avec un unique test, nous pouvions vérifier que le composant et le service interagissent correctement ensemble, tout en validant unitairement leur comportement.
Cependant, si le mocking des dépendances (services, providers, etc.) via la méthode avancée rend le test unitaire trop complexe ou difficile à maintenir, il est préférable de revenir à la méthode simple pour simplifier le code des tests.
Conclusion
En conclusion, le mocking de services dans Angular est une compétence essentielle pour écrire des tests unitaires efficaces et robustes. En utilisant les méthodes décrites dans cet article, vous pouvez isoler les dépendances de vos composants et garantir leur bon fonctionnement dans divers scénarios. Que vous optiez pour la méthode simple ou avancée, l’important est de comprendre les avantages et les inconvénients de chaque approche et de les appliquer judicieusement selon le contexte de vos tests.
Pour aller plus loin et approfondir vos connaissances sur les tests unitaires et les fondamentaux de la programmation d’applications, je vous invite à consulter mon livre Apprendre à programmer ses premières applications. Vous y trouverez des conseils pratiques, des exemples concrets et toutes les bases nécessaires au développement logiciel.