Appearance
HttpTestingController : Tester les requêtes HTTP
L'HttpTestingController est un utilitaire Angular qui permet d'intercepter et de simuler les requêtes HTTP dans vos tests unitaires. C'est comme avoir un contrôleur de trafic pour vos appels API !
Pourquoi utiliser HttpTestingController ?
Imaginez que vous testez une application de météo. Sans HttpTestingController, votre test ferait un vrai appel à l'API météo, ce qui serait lent, coûteux et imprévisible. Avec HttpTestingController, vous contrôlez exactement ce que "l'API" renvoie, comme si vous étiez le serveur vous-même.
Les concepts clés
1. HttpTestingController
C'est l'outil principal qui intercepte toutes les requêtes HTTP de votre application pendant les tests. Il vous permet de :
- Vérifier quelles requêtes ont été faites
- Contrôler les réponses retournées
- Simuler des erreurs réseau
2. httpMock.expectOne()
Cette méthode attrape une requête HTTP spécifique et vous donne le contrôle total sur sa réponse :
typescript
// Intercepte la requête vers /users
const req = httpMock.expectOne('/users');
// Vérifie que c'est bien une requête GET
expect(req.request.method).toBe('GET');
// Vérifie le corps de la requête (pour POST/PUT)
expect(req.request.body).toEqual({ name: 'John' });
3. req.flush() - Simuler une réponse réussie
typescript
// Simule une réponse du serveur
req.flush([
{ id: 1, name: 'John Doe' },
{ id: 2, name: 'Jane Smith' }
]);
4. req.error() - Simuler une erreur réseau
typescript
// Simule une erreur réseau (pas d'internet, serveur inaccessible)
req.error(new ProgressEvent('error'));
5. httpMock.verify() - Vérifier que tout est testé
typescript
afterEach(() => {
httpMock.verify();
});
Cette méthode garantit qu'aucune requête HTTP n'est restée en attente. Si votre code fait 2 requêtes mais que vous n'avez testé qu'une seule, le test échoue.
Exemple complet
Créons un composant qui affiche une liste d'utilisateurs :
typescript
import { Component, OnInit, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Component({
selector: 'app-user-list',
standalone: true,
template: `
<h1>Liste des utilisateurs</h1>
<ul>
@for (user of users; track user.id) {
<li>{{ user.name }}</li>
}
</ul>
`
})
export class UserListComponent implements OnInit {
private http = inject(HttpClient);
users: User[] = [];
ngOnInit() {
// Récupère la liste des utilisateurs depuis l'API
this.http.get<User[]>('/users')
.subscribe(users => {
this.users = users;
});
}
}
Maintenant, testons ce composant avec HttpTestingController :
typescript
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';
import { provideHttpClient } from '@angular/common/http';
import { UserListComponent } from './user-list.component';
describe('UserListComponent', () => {
let component: UserListComponent;
let fixture: ComponentFixture<UserListComponent>;
let httpMock: HttpTestingController;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [UserListComponent],
providers: [
provideHttpClient(),
provideHttpClientTesting()
]
}).compileComponents();
fixture = TestBed.createComponent(UserListComponent);
component = fixture.componentInstance;
httpMock = TestBed.inject(HttpTestingController);
});
afterEach(() => {
// Vérifie qu'aucune requête HTTP n'est restée en attente
httpMock.verify();
});
it('affiche la liste des utilisateurs à l\'init', () => {
// 1. Données factices pour simuler la réponse de l'API
const usersMock = Array.from({ length: 10 }, (_, i) => ({
id: i + 1,
name: `User ${i + 1}`
}));
// 2. Lance ngOnInit (et donc l'appel HTTP du composant)
fixture.detectChanges();
// 3. Intercepte la requête vers /users
const req = httpMock.expectOne('/users');
expect(req.request.method).toBe('GET');
// 4. Renvoie la réponse factice
req.flush(usersMock);
// 5. Met à jour le template après la réponse
fixture.detectChanges();
// 6. Vérifie que les utilisateurs s'affichent dans le DOM
const li = fixture.nativeElement.querySelectorAll('li');
expect(li.length).toBe(10);
expect(li[0].textContent).toBe('User 1');
});
});
Tester les erreurs
Vous pouvez aussi tester comment votre composant gère les erreurs :
typescript
it('gère les erreurs de réseau', () => {
fixture.detectChanges();
const req = httpMock.expectOne('/users');
// Simule une erreur réseau
req.error(new ProgressEvent('error'));
fixture.detectChanges();
// Vérifie que le composant gère l'erreur correctement
expect(component.users).toEqual([]);
});
Tester les requêtes POST
Pour tester l'envoi de données :
typescript
it('envoie un nouvel utilisateur', () => {
const newUser = { name: 'John Doe' };
// Simule l'ajout d'un utilisateur
component.addUser(newUser);
const req = httpMock.expectOne('/users');
expect(req.request.method).toBe('POST');
expect(req.request.body).toEqual(newUser);
// Simule la réponse du serveur
req.flush({ id: 1, ...newUser });
});
Bonne pratique
Toujours appeler httpMock.verify()
dans afterEach()
pour s'assurer que toutes les requêtes HTTP ont été testées. Cela évite les tests qui passent par hasard !
Attention
N'oubliez pas d'importer provideHttpClientTesting()
dans vos providers. Sans cela, HttpTestingController ne fonctionnera pas !
Résumé
L'HttpTestingController vous donne un contrôle total sur vos requêtes HTTP dans les tests :
expectOne()
→ Attrape une requête spécifiqueflush()
→ Simule une réponse réussieerror()
→ Simule une erreur réseauverify()
→ Garantit que tout a été testé
C'est l'outil parfait pour tester vos composants qui communiquent avec des APIs, sans dépendre de vrais serveurs externes !