CHAPITRE 12 : TESTER ET DEPLOYER

Dernier chapitre : s'assurer que l'application fonctionne, puis la mettre en ligne.

1. Les niveaux de test

TypePorteeVitesse
UnitaireUne classe isoleeTres rapide
IntegrationPlusieurs couchesMoyenne
Bout en boutToute l'applicationLente

La proportion recommandee : beaucoup d'unitaires, quelques tests d'integration, tres peu de bout en bout.

2. Un test unitaire

@ExtendWith(MockitoExtension.class)
class EtudiantServiceTest {

    @Mock
    private EtudiantRepository repository;

    @InjectMocks
    private EtudiantService service;

    @Test
    void doit_lever_une_exception_si_etudiant_absent() {

        when(repository.findById(99L)).thenReturn(Optional.empty());

        assertThrows(EtudiantIntrouvableException.class,
                     () -> service.trouverParId(99L));
    }

    @Test
    void doit_filtrer_les_admis() {

        when(repository.findAll()).thenReturn(List.of(
            new Etudiant("Awa",   "19A123", 15.5),
            new Etudiant("Brice", "19A124",  9.0)
        ));

        List<Etudiant> admis = service.listerAdmis();

        assertEquals(1, admis.size());
        assertEquals("Awa", admis.get(0).getNom());
    }
}

Le @Mock remplace le repository par un faux : le test ne touche aucune base et s'execute en quelques millisecondes.

3. Tester un controleur

@WebMvcTest(EtudiantController.class)
class EtudiantControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private EtudiantService service;

    @Test
    void doit_retourner_la_liste() throws Exception {

        when(service.listerTous()).thenReturn(List.of(
            new Etudiant("Awa", "19A123", 15.5)
        ));

        mockMvc.perform(get("/api/etudiants"))
               .andExpect(status().isOk())
               .andExpect(jsonPath("$[0].nom").value("Awa"));
    }

    @Test
    void doit_refuser_un_nom_vide() throws Exception {

        mockMvc.perform(post("/api/etudiants")
                .contentType(MediaType.APPLICATION_JSON)
                .content("{\"nom\":\"\",\"matricule\":\"19A123\"}"))
               .andExpect(status().isBadRequest());
    }
}

@WebMvcTest ne demarre que la couche web : le test reste rapide.

4. Un test d'integration

@SpringBootTest
@AutoConfigureMockMvc
@ActiveProfiles("test")
class ParcoursCompletTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    void doit_creer_puis_relire_un_etudiant() throws Exception {

        mockMvc.perform(post("/api/etudiants")
                .contentType(MediaType.APPLICATION_JSON)
                .content("{\"nom\":\"Awa Ngono\",\"matricule\":\"19A123\"}"))
               .andExpect(status().isCreated());

        mockMvc.perform(get("/api/etudiants"))
               .andExpect(status().isOk())
               .andExpect(jsonPath("$.length()").value(1));
    }
}

5. Lancer les tests

./mvnw test
./mvnw test -Dtest=EtudiantServiceTest

6. Packager l'application

./mvnw clean package

# produit target/gestion-etudiants-0.0.1-SNAPSHOT.jar

java -jar target/gestion-etudiants-0.0.1-SNAPSHOT.jar

Ce JAR contient tout : votre code, les dependances et le serveur. Une machine avec Java suffit pour l'executer.

# ignorer les tests (a eviter en temps normal)
./mvnw clean package -DskipTests

7. Docker

# Dockerfile
FROM eclipse-temurin:17-jre-alpine

WORKDIR /app
COPY target/*.jar app.jar

EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
docker build -t gestion-etudiants .

docker run -p 8080:8080 \
  -e SPRING_PROFILES_ACTIVE=prod \
  -e DB_URL=jdbc:mysql://db:3306/ecole \
  -e DB_PASSWORD=secret \
  gestion-etudiants

Les secrets passent par -e, jamais par le Dockerfile : une image peut etre partagee, et tout ce qu'elle contient avec.

8. Avant de mettre en ligne

  • ddl-auto sur validate ou none
  • Aucun secret dans le code ni dans l'historique Git
  • Journalisation en WARN ou INFO, pas DEBUG
  • La console H2 desactivee
  • CORS limite aux origines reellement attendues
  • Les traces techniques masquees dans les reponses
  • Une sauvegarde de la base en place

Et maintenant ?

Vous disposez des fondations : injection de dependances, API REST, persistance, relations, validation, securite, tests et deploiement.

Pour poursuivre : l'authentification par jeton JWT, la pagination, la documentation avec OpenAPI, le cache, les traitements asynchrones, et les microservices.

Mais surtout, construisez un vrai projet. Une gestion de bibliotheque, un suivi de notes, une petite boutique. C'est en affrontant des problemes concrets que tout cela prend sens.

A retenir

  • Beaucoup de tests unitaires, peu de tests lents
  • @WebMvcTest pour un controleur, @SpringBootTest pour un parcours complet
  • Le JAR produit est autonome
  • Les secrets passent par l'environnement, jamais par l'image

Exercices d'application

Exercice 1. Tester un serviceEcrivez deux tests unitaires pour votre service avec @Mock : un cas nominal et un cas d'erreur.
Exercice 2. Tester un controleurAvec @WebMvcTest et MockMvc, verifiez qu'un GET renvoie 200 et qu'un POST invalide renvoie 400.
Exercice 3. PackagerProduisez le JAR, lancez-le avec java -jar, puis ecrivez le Dockerfile et construisez l'image.

Cherchez avant de regarder la solution : c'est en butant sur les difficultes que l'on progresse.

Questions / Réponses

Aucune question. Soyez le premier à poser une question.
★ ★ ★ ★ ★
2 votes. Moyenne 5 sur 5.

Ajouter un commentaire

Anti-spam