Créer des tests unitaires avec Angular [Part 3] : Tests d'intégration

Jusqu'à présent, les tests que nous faisions ne prenaient pas en compte le framework Angular. Ils ont l'avantage d'être très rapide mais ne reflettent pas complètement l'environnement dans lequel vos classes/services s'exécuteront.

Cette fois, c'est le Framework Angular qui nous propose un environnement de test qui tiendra compte par exemple de l'injection de dépendance. Il permet également de tester les templates de ses pages comme par exemple si le binding s'effectue correctement. cela nous permettra donc de tester des composants.

Quand vous demandez à Angular CLI de générer un service, une classe, c'est cette approche qui est privilégiée. Par exemple, si vous faites :

ng g s users

le fichier usersService.spec.ts aura l'aspect suivant :

import { TestBed, inject } from '@angular/core/testing';
import { UsersService } from './users.service';

describe('UsersService', () => {
  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [UsersService]
    });
  });

  it('should be created', inject([UsersService], (service: UsersService) => {
    expect(service).toBeTruthy();
  }));
});

Ce code peut également s'écrire de cette façon :

import { UsersService } from './users.service';
import { TestBed } from '@angular/core/testing';

describe('UsersService', () => {

  let usersService: UsersService;

  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [UsersService]
    });

    usersService = TestBed.get(UsersService);
  });

  it('should be created', () => {
    expect(usersService).toBeTruthy();
  });
});

Le premier point est que nous utilisons le TestBed fournit par Angular. Tout comme votre app.Module, on doit lui fournir les informations initiales comme les providers, les imports, etc. Sa méthode get permet de récupérer l'instance d'un service en utilisant le même service d'injection d'Angular. Donc dans le code ci-dessus, le test plantera parce qu'il ne sait pas ce qu'est un HttpClient (qui est dans le constructeur du service. Nous allons lui fournir et comme nous voulons le 'mocker', on va utiliser un utilitaire fournir dans les outils d'Angular, le HttpTestingController :

import { UsersService } from './users.service';
import { TestBed, inject } from '@angular/core/testing';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { IUser } from '../entities/user';

describe('UsersService', () => {

  let usersService: UsersService;
  let httpTestingController: HttpTestingController;
beforeEach(() => { TestBed.configureTestingModule({ imports: [HttpClientTestingModule], providers: [UsersService, HttpTestingController] }); httpTestingController = TestBed.get(HttpTestingController);
usersService = TestBed.get(UsersService); }); it('should be created', () => { expect(usersService).toBeTruthy(); }); });

Cette fois ci, notre test fonctionne ;-) On teste bien ici que le HttpClient est bien passé en argument dans le constructeur de notre service, bref, on a testé que l'injection fonctionnait bien.

Maintenant, testons comme précédemment si l'appel http est correct, que l'url est correcte :

  it('appel avec la bonne url', () => {
    const user: IUser = { id: 1, firstName: 'Richard', lastName: 'Clark' };

    usersService.getUser(1)
      .subscribe((resp: IUser) => {
        expect(resp).toBe(user);
      });

    const mockRequest = mockHttp.expectOne('https://monSite.fr/api/users/1');
    expect(mockRequest.request.method).toBe('GET');
    mockRequest.flush(user);

    mockHttp.verify();
  });

La méthode expectOne indique qu'elle url doit être appelée. Vous voyez qu'on peut tester également si c'est bien la méthode GET qui est utilisée. flush indique ce qui doit être retournée. On peut aussi spécifier des informations comme les headers. Enfin, verify s'assure qu'il n'y a rien eu d'autres comme appel, autre que ce que l'on a testé.

Dernier petit point intéressant, on peut injecter un service au niveau de la méthode de test. Ainisi, au lieu de déclarer une variable global usersService, on peut le faire grâce à la méthode inject du framework de test d'angular. Le test unitaire complet peut donc ressembler à cela :

import { UsersService } from './users.service';
import { TestBed, inject } from '@angular/core/testing';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { IUser } from '../entities/user';

describe('UsersService', () => {
  let mockHttp: HttpTestingController;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [HttpClientTestingModule],
      providers: [UsersService]
    });

    mockHttp = TestBed.get(HttpTestingController);
  });

  it('should be created', inject([UsersService], (service: UsersService) => {
    expect(service).toBeTruthy();
  }));

  it('appel avec la bonne url', inject([UsersService], (service: UsersService) => {
    const user: IUser = { id: 1, firstName: 'Richard', lastName: 'Clark' };

    service.getUser(1)
      .subscribe((resp: IUser) => {
        expect(resp).toBe(user);
      });

    const mockRequest = mockHttp.expectOne('https://monSite.fr/api/users/1');
    expect(mockRequest.request.method).toBe('GET');
    mockRequest.flush(user);

    mockHttp.verify();
  }));
});

blog comments powered by Disqus