Skip to content

Vous souhaitez recevoir de l'aide sur ce sujet ? rejoignez la communauté Angular.fr sur Discord.

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écifique
  • flush() → Simule une réponse réussie
  • error() → Simule une erreur réseau
  • verify() → 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 !