Exercice 2 / Étape 4 — Tests Jest du front-end¶
Couverture de tous les services, composants, guard et intercepteur du front-end (Exercice 1, étapes 3 et 5), en suivant le plan de tests.
Fichiers de test ajoutés / étendus¶
| Fichier | Nature |
|---|---|
core/service/user.service.spec.ts (étendu) |
HttpTestingController : register, login (+ stockage du token), logout, getToken, isLoggedIn |
core/service/student.service.spec.ts (nouveau) |
HttpTestingController : findAll/findById/create/update/delete — vérifie verbe HTTP, URL, corps |
core/guards/auth.guard.spec.ts (nouveau) |
TestBed.runInInjectionContext() — connecté → true, non connecté → false + redirection /login |
core/interceptors/auth.interceptor.spec.ts (nouveau) |
header Authorization ajouté si token présent, absent sinon |
pages/login/login.component.spec.ts (étendu) |
soumission valide/invalide, succès (navigation), échec (message d'erreur) |
pages/register/register.component.spec.ts (étendu) |
soumission valide/invalide, succès (navigation), onReset() |
pages/students/student-list/student-list.component.spec.ts (étendu) |
chargement, erreur de chargement, suppression (confirmée / annulée / en échec) |
pages/students/student-detail/student-detail.component.spec.ts (étendu) |
chargement par id de route, étudiant introuvable, suppression |
pages/students/student-form/student-form.component.spec.ts (étendu) |
mode création (pas d'id de route) vs. mode édition (id de route, préremplissage), succès/échec |
Changement de pattern de DI dans les tests¶
Les specs déjà en place (register, login, student-*) utilisaient
{ provide: X, useValue: SomeMockServiceClass } — la classe elle-même
comme valeur, pas une instance. Ça ne posait pas de problème tant que le
test se contentait de expect(component).toBeTruthy(), mais devient
invalide dès qu'on veut vérifier qu'une méthode a été appelée ou contrôler
sa valeur de retour (SomeMockServiceClass.method est undefined, ce
n'est pas une méthode d'instance). Remplacé par un objet
{ method: jest.fn() } explicite par test, ou par
HttpTestingController/provideHttpClientTesting() pour les services —
plus explicite et permet d'asserter précisément les appels.
Bug réel trouvé en écrivant les tests E2E (voir aussi étape 5)¶
En écrivant le test Cypress du cas d'erreur de connexion, le message de
la réponse HTTP /api/login ("Invalid credentials") ne s'affichait
jamais — seul le message générique de secours apparaissait. Cause :
UserService.login() appelle /api/login avec responseType: 'text'
(nécessaire car le succès renvoie un JWT brut, pas du JSON). Angular
applique ce responseType aussi aux réponses d'erreur : error.error
reste une chaîne brute, jamais parsée en JSON — donc
error.error?.message valait toujours undefined, y compris pour un vrai
message d'erreur JSON renvoyé par le back-end.
// src/app/pages/login/login.component.ts — avant
error: (error: HttpErrorResponse) => {
this.loading = false;
this.errorMessage = error.error?.message ?? 'Login failed. Please check your credentials and try again.';
}
// src/app/pages/login/login.component.ts — après
error: (error: HttpErrorResponse) => {
this.loading = false;
this.errorMessage = this.extractErrorMessage(error);
}
private extractErrorMessage(error: HttpErrorResponse): string {
const fallback = 'Login failed. Please check your credentials and try again.';
const body = error.error;
if (typeof body === 'string') {
try {
return JSON.parse(body)?.message ?? fallback;
} catch {
return fallback;
}
}
return body?.message ?? fallback;
}
Ce bug n'avait pas été détecté à l'étape 3 (Exercice 1) car la
vérification s'était arrêtée à la couche HTTP (curl), sans passer par le
composant Angular réel dans un navigateur — exactement le type de
régression que les tests (ici, Cypress) sont censés attraper. Tests
ajoutés côté Jest (login.component.spec.ts) pour les trois cas : message
JSON valide (dans une chaîne), corps non-JSON, corps absent.
Couverture de code¶
jest.config.js :
module.exports = {
preset: 'jest-preset-angular',
roots: ['<rootDir>/src/'],
testMatch: ['**/+(*.)+(spec).+(ts|js)'],
setupFilesAfterEnv: ['<rootDir>/setup-jest.ts'],
collectCoverage: true,
- coverageReporters: ['html'],
+ coverageReporters: ['html', 'text', 'text-summary'],
+ coverageThreshold: {
+ global: {
+ statements: 80,
+ branches: 80,
+ functions: 80,
+ lines: 80,
+ },
+ },
};
Résultat (npm test, suite complète) :
| Métrique | Résultat |
|---|---|
| Statements | 100 % (251/251) |
| Branches | 85.71 % (18/21) |
| Functions | 100 % (47/47) |
| Lines | 100 % (233/233) |
| Suites / Tests | 10 suites, 52 tests, tous verts |
Toutes les métriques dépassent le seuil de 80 % exigé — coverageThreshold
fait échouer npm test si une régression fait redescendre la couverture
sous ce seuil.
Comment reproduire¶
Rapport HTML détaillé : front-end/coverage/etudiant-frontend/index.html
(à ouvrir dans un navigateur).
Pour lancer un seul fichier de test (plus rapide en cours de développement) :
Points de vigilance restants¶
- Les templates HTML ne sont pas testés indépendamment (Jest les couvre indirectement via les composants) — pas de test de rendu visuel.
RestExceptionHandlercôté back (Exercice 1) renvoie un JSON classique pour les erreurs — le bug ci-dessus est spécifique au front, à la combinaisonresponseType: 'text'+ gestion d'erreur.