Skip to content

Tester un service

Un service peut être simple comme il peut être très complexe. Prenons ici un exemple d'un service qui utilise un autre service comme dépendance.

// Extrait du service testé (services/adoption-service.ts)
export class AdoptionService {

  chatService: ChatService = inject(ChatService);

 ... 
}

Il faut importer le service testé et sa dépendance dans les tests en les inscrivant comme providers du module de test et en les injectant dans TestBed. On les inscrit dans providers comme il s'agit de services. Les modules vont dans imports et les composants dans declarations.

// Extrait du fichier de tests (services/adoption-service.spec.ts)
describe('AdoptionService', () => {
  let service: AdoptionService;
  let chatService: ChatService;

  beforeEach(() => {
    // Indiquer les services utilisés dans providers
    TestBed.configureTestingModule({
      providers: [
        AdoptionService,
        ChatService,
      ]
    });
    service = TestBed.inject(AdoptionService);
    chatService = TestBed.inject(ChatService);
  });

...
}

Créer des simulacres espion des dépendances

La grande majorité du temps, on voudra simuler les dépendances dans les tests. On veut s'assurer que c'est bien le service qu'on teste et non sa dépendance. On simulera toutes les dépendances incluant celles qu'on a créé nous même.

Nous créerons donc de « fausses » dépendances qui vont espionner les vraies. Quand les méthodes espionnées seront appelées, leur appel sera interrompu et les méthodes espionnées seront remplacées par les fausses (mock).

  • On crée un objet espion à l'aide de la méthode createSpyObj de Jasmine. Cette méthode prend en paramètre la classe à espionner et les méthodes de la classe à espionner.
chatServiceSpy = jasmine.createSpyObj('ChatService', ['getChats']);
  • Dans la section providers du module de test, changer la dépendance par { provide : NomDépendance, useValue : nomEspion }
beforeEach(() => {
    chatServiceSpy = jasmine.createSpyObj('ChatService', ['getChats']);

    TestBed.configureTestingModule({
      providers: [
        AdoptionService,
        {provide: ChatService, useValue: chatServiceSpy},
      ]
    });
    service = TestBed.inject(AdoptionService);
    chatServiceSpy = TestBed.inject(ChatService) as jasmine.SpyObj<ChatService>;
  });

Avec des requêtes HTTP

Pour tester les différentes requêtes HTTP, nous pourrons utiliser des ressources d'Angular au lieu d'espionner HttpClient.

Dans la section providers on ajoutera

  • provideHttpClient(),
  • provideHttpClientTesting()
describe('FilmService', () => {
  let service: FilmService;
  // Variable pour simuler les appels HTTP
  let httpTesting: HttpTestingController;

  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [
        FilmService,
        // Providers à ajouter
        provideHttpClient(),
        provideHttpClientTesting()
      ],
    });
    service = TestBed.inject(FilmService);
    // Injecter le contrôleur de test
    httpTesting = TestBed.inject(HttpTestingController);
  });
  • Ceci nous permettra d'intercepter les requêtes HTTP faites par le service et d'en contrôler les valeurs de retour.

Prenons le test suivant comme exemple.

it('getAllFilms devrait retourner liste de films fournie par l\'api', () => {
    service.getAllFilms().subscribe({
      next: (reponse) => {
        expect(reponse).toEqual(mockFilmsList);
      },
      error: fail
    });

    const req = httpTesting.expectOne(url);
    expect(req.request.method).toBe('GET');

    req.flush(mockFilmsList);
  });
  • On utilise le service pour faire l'appel, sans espion
  • Ligne #4: lorsque la réponse sera transmise, on vérifie que le contenu reçu est bien le contenu renvoyé de l'API. Dans le cas d'un test, une liste de données simulées.
  • Ligne #9: On intercepte la requête et vérifie l'adresse apellée
  • Ligne #10: On s'assure que c'est la bonne méthode HTTP qui est appellée.
  • Ligne #12: On simule la réponse. Elle retourne mockFilmsList. La réponse est donc reçue et la vérification de la ligne #4 peut avoir lieu.

Tester pour les cas d'erreur

On peut également tester pour les cas d'erreurs dans les requêtes HTTP.

it('should return an error when the server returns a 500', () => {
    service.getAllFilms().subscribe({
      next: () => fail('Une erreur devrait être levée'),
      error: (error) => {
        expect(error.status).toBe(500);
        expect(error.statusText).toBe('Erreur interne');
      }
    });

    const req = httpTesting.expectOne(url);

    // Retourner un erreur
    req.flush('Erreur!', {
      status: 500,
      statusText: 'Erreur interne'
    });
  });

Références

Documentation d'Angular - Tester le client HTTP