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
createSpyObjde Jasmine. Cette méthode prend en paramètre la classe à espionner et les méthodes de la classe à espionner.
- Dans la section
providersdu 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.
- 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'
});
});