Exercice 2 / Étape 3 — Implémentation des tests back-end¶
Complète les classes de test existantes (voir analyse)
et en ajoute de nouvelles, en suivant le plan de tests,
pour couvrir l'authentification (login/JWT) et le CRUD étudiants
(Exercice 1, étapes 4-5) restés non testés.
Rapport de couverture (JaCoCo)¶
Ajouté à pom.xml (aucun outil de couverture n'était configuré
auparavant) :
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.12</version>
<executions>
<execution>
<id>prepare-agent</id>
<goals><goal>prepare-agent</goal></goals>
</execution>
<execution>
<id>report</id>
<phase>test</phase>
<goals><goal>report</goal></goals>
</execution>
<execution>
<id>check</id>
<phase>verify</phase>
<goals><goal>check</goal></goals>
<configuration>
<rules>
<rule>
<element>BUNDLE</element>
<limits>
<limit>
<counter>LINE</counter>
<value>COVEREDRATIO</value>
<minimum>0.80</minimum>
</limit>
</limits>
</rule>
</rules>
</configuration>
</execution>
</executions>
</plugin>
report (phase test) génère target/site/jacoco/index.html sur un
simple mvn clean test. check (phase verify, donc mvn clean verify)
fait échouer le build si la couverture globale (lignes) descend sous
80 % — ajouté seulement une fois le seuil atteint, pour ne pas casser le
build pendant l'écriture itérative des tests.
Résultat final (mvn clean verify)¶
| Package | Lignes couvertes | Total | % |
|---|---|---|---|
service (JwtService, StudentService, UserService) |
60 | 60 | 100 % |
controller (StudentController, UserController) |
17 | 17 | 100 % |
mapper (UserDtoMapperImpl, StudentDtoMapperImpl) |
26 | 29 | 89,7 % |
| Ensemble du projet (BUNDLE, toutes classes) | 209 | 230 | 90,9 % |
Seuil de 80 % largement atteint sur les couches ciblées par l'exercice
(service/controller). Le reste (DTO, entités, configuration Spring)
est couvert incidemment par le démarrage du contexte Spring dans les
tests d'intégration (@SpringBootTest), sans tests dédiés — cohérent
avec la consigne de se concentrer sur services et controllers.
Tests ajoutés¶
UserServiceTest.java (complété)¶
Ajout d'un mock JwtService (nécessaire pour login()) et de 3 méthodes,
mêmes conventions que l'existant (@Mock/@InjectMocks, nommage
test_<action>_<condition>_<résultat>) :
test_login_valid_credentials_returns_token— cas nominal du plan.test_login_wrong_password_throws_IllegalArgumentExceptiontest_login_unknown_login_throws_IllegalArgumentException
(Les deux derniers dépassent le plan initial — nécessaires pour couvrir
les deux branches du if dans UserService.login().)
JwtServiceTest.java (nouveau)¶
JwtService lit secret/expirationMs via @Value, indisponible hors
contexte Spring : injectés directement avec
ReflectionTestUtils.setField(). 4 tests :
test_generateToken_returns_non_null_tokentest_extractUsername_returns_original_logintest_isTokenValid_matching_user_returns_truetest_isTokenValid_mismatched_user_returns_false(hors plan — couvre la seconde branche du&&dansisTokenValid())
StudentServiceTest.java (nouveau, mêmes conventions que UserServiceTest)¶
8 tests couvrant les 5 méthodes du service :
test_create_student,test_findAll_returns_all_students,test_findById_existing_id_returns_student,test_update_student,test_delete_student— cas nominaux du plan.test_create_student_with_existing_email_throws_IllegalArgumentException,test_update_student_with_email_used_by_another_student_throws_IllegalArgumentException,test_findById_unknown_id_throws_NoSuchElementException— hors plan, ajoutés pour couvrir la vérification d'unicité d'email ajoutée à l'étape 4 de l'Exercice 1 et le cas 404.
UserControllerTest.java (complété)¶
2 tests d'intégration POST /api/login, mêmes conventions Testcontainers
que l'existant :
loginUserSuccessful— inscrit un utilisateur puis se connecte, vérifie 200 + corps non vide (le JWT).loginUserWithWrongPassword— 400 (hors plan — sans ce cas, la branche d'erreur du controller n'aurait aucune couverture).
StudentControllerTest.java (nouveau, mêmes conventions Testcontainers/MockMvc)¶
En @BeforeEach, un utilisateur est inscrit puis connecté via de vrais
appels MockMvc pour obtenir un JWT authentique — exerce toute la chaîne
de sécurité (JwtAuthenticationFilter inclus), pas un contexte de
sécurité simulé. 7 tests :
createStudentSuccessful,findAllStudents,findStudentByIdSuccessful,updateStudentSuccessful,deleteStudentSuccessful— cas nominaux du plan, avecAuthorization: Bearer <token>.createStudentWithoutTokenIsUnauthorized— cas de sécurité prévu au plan (401).findStudentByUnknownIdReturnsNotFound— hors plan, ajouté pour couvrir la branche 404 deStudentService.findById().
Comment reproduire (pour l'examinateur)¶
cd back-end
# Suite complète (30 tests) + rapport de couverture, sans gate
mvn clean test
# -> target/site/jacoco/index.html (ouvrir dans un navigateur)
# -> target/site/jacoco/jacoco.csv (données brutes)
# Suite complète + vérification du seuil de 80 % (échoue si < 80 %)
mvn clean verify
# Une seule classe de test
mvn test -Dtest=StudentControllerTest
mvn test -Dtest=JwtServiceTest
Prérequis : Docker Desktop lancé (Testcontainers pour
UserControllerTest/StudentControllerTest) — voir
étape 2 pour le détail des deux fixes
nécessaires (socket Docker Desktop + api.version=1.41), déjà en place
dans pom.xml.
Vérifications effectuées¶
| Commande | Résultat |
|---|---|
Chaque nouvelle classe de test, isolément (mvn test -Dtest=...) |
verte avant d'écrire la suivante |
mvn clean test (suite complète) |
30/30, BUILD SUCCESS |
mvn clean verify (avec gate JaCoCo 80 %) |
30/30 + seuil respecté, BUILD SUCCESS |
Points de vigilance restants¶
RestExceptionHandler/ErrorDetailsrestent partiellement couverts (branchesBadCredentialsException/AccessDeniedException/Exceptiongénérique jamais déclenchées par les scénarios actuels) — sans impact sur le seuil global (90,9 %), mais à noter si l'examinateur demande une couverture par classe plutôt que globale.