Skip to content

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_IllegalArgumentException
  • test_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_token
  • test_extractUsername_returns_original_login
  • test_isTokenValid_matching_user_returns_true
  • test_isTokenValid_mismatched_user_returns_false (hors plan — couvre la seconde branche du && dans isTokenValid())

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, avec Authorization: Bearer <token>.
  • createStudentWithoutTokenIsUnauthorized — cas de sécurité prévu au plan (401).
  • findStudentByUnknownIdReturnsNotFound — hors plan, ajouté pour couvrir la branche 404 de StudentService.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/ErrorDetails restent partiellement couverts (branches BadCredentialsException/AccessDeniedException/Exception gé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.