Skip to content

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

cd front-end
npm install                       # si pas déjà fait
npm test                          # = npx jest --coverage, seuil 80% appliqué

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) :

npx jest src/app/pages/login/login.component.spec.ts

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.
  • RestExceptionHandler côté back (Exercice 1) renvoie un JSON classique pour les erreurs — le bug ci-dessus est spécifique au front, à la combinaison responseType: 'text' + gestion d'erreur.