Aller au contenu

Tests avec Spring Boot

Test unitaire pur (sans Spring)

But : tester une classe isolée sans démarrer le contexte Spring.

Pour les exemples de codes, je reprends souvent le TP Waouf Waouf. Dans le code ci-dessous, je ne teste pas les routes HTTP.

import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;

class PublicControllerTest {

    @Test
    void testGetAllChiens() {
        // Mocks
        ChienRepository mockRepo = Mockito.mock(ChienRepository.class);
        Mockito.when(mockRepo.findAll()).thenReturn(List.of(new Chien("Rex")));

        // Injection manuelle dans le contrôleur
        PublicController controller = new PublicQueryController(mockRepo, null);

        // Exécution et vérification
        List<Chien> result = controller.getAllChiens();
        assertThat(result).hasSize(1);
        assertThat(result.get(0).getNom()).isEqualTo("Rex");
    }
}

Test d’intégration Web (MockMvc)

But : tester le contrôleur via des requêtes HTTP simulées.

Dans ce code ci-dessous, je teste toute la couche Web (controllers, mappings, JSON), ce qui est plutôt rapide car Spring ne charge pas tout le contexte !

import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.web.servlet.MockMvc;

@WebMvcTest(PublicController.class)
class PublicControllerIntegrationTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private ChienRepository chienRepository;

    @Test
    void shouldReturnListOfChiens() throws Exception {
        when(chienRepository.findAll()).thenReturn(List.of(new Chien("Milou")));

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

Test de bout en bout (@SpringBootTest)

But : tester l’application entière (Web + Service + Repository + DB).

C’est l’équivalent d’un test avec un vrai serveur (Tomcat embarqué), c’est plus lent (quelques secondes), mais bon… c’est pratique !

import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.ResponseEntity;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class ApplicationIntegrationTest {

    @Autowired
    private TestRestTemplate restTemplate;

    @Test
    void shouldReturnStatus200() {
        ResponseEntity<String> response = restTemplate.getForEntity("/api/public/chiens", String.class);
        assertThat(response.getStatusCode().value()).isEqualTo(200);
    }
}

Récapitulatif

Type de test Annotation Démarrage Spring ? Cible Vitesse
Unitaire (aucune) Non Logique interne Très rapide
Intégration Web @WebMvcTest Oui (Web seulement) Contrôleur / JSON Rapide
End-to-End @SpringBootTest Oui (complet) Toute l’application Lent

Quelques conseils

Astuce Pourquoi
Nommer les tests lisiblement shouldReturn404WhenAdherentNotFound() : le nom raconte ce qu’il fait
Un test = une idée Éviter les “méga-tests” à 200 lignes
Tester les erreurs aussi ! Vérifier que les erreurs HTTP et validations fonctionnent
Limiter les tests SpringBootTest Garder les pour les cas d’intégration réelle
Favoriser MockMvc + Mockito Pour des tests rapides et efficaces
Ne jamais tester les frameworks eux-mêmes On teste son code, pas Spring !

Exemple de structure d’organisation (Tests)

src/
 ├── main/java/...   pour le code principal
 └── test/java/...   pour les tests unitaires et d'intégration
      ├── controller/
      ├── service/
      ├── repository/
      └── ApplicationIntegrationTest.java

Quelques outils et librairies

Voici un tableau d’une lite d’outils qui permettent de tout tester, du simple service au comportement complet d’une application dans un environnement réaliste.

Outil Rôle Annotation / Exemple
JUnit 5 (Jupiter) Framework de tests standard @Test, @BeforeEach, @Nested, etc.
Mockito Créer des mocks (objets simulés) Mockito.mock(), when(...).thenReturn(...)
AssertJ Assertions lisibles et fluides assertThat(result).hasSize(3).contains("Rex");
Spring Boot Test Démarrage du contexte Spring @SpringBootTest, @WebMvcTest
MockMvc Simulation de requêtes HTTP mockMvc.perform(get("/api/chiens"))
Hamcrest Assertions avancées assertThat(value, is(notNullValue()))
Testcontainers Lance des BDD réelles (PostgreSQL, Redis…) dans des containers Docker pour les tests @Testcontainers, @Container
WireMock Simuler des APIs externes dans les tests stubFor(get("/api/externe").willReturn(okJson(...)))

Bases de données pour les tests

L’idéal est d’utiliser PostgreSQL ou MySQL en PROD et H2 ou Testcontainers en TEST selon le niveau d’intégration.

Type Utilisation Exemple
H2 Base en mémoire ultra-rapide pour les tests spring.datasource.url=jdbc:h2:mem:testdb
Testcontainers Lancer une vraie base Docker (PostgreSQL, MySQL…) pour un test d’intégration complet @Container PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16");

OUtils de couverture de code

En voici quelques uns, il y en a beaucoup, personnellement, j’utilise Eclemma que j’explique dans un autre cours depuis la page d’accueil.

Outil Description Intégration
JaCoCo Génère des rapports de couverture de tests Plugin Maven/Gradle
SonarQube / SonarCloud Analyse la qualité du code (complexité, duplications, bugs) CI/CD GitLab
SpotBugs Analyse statique pour détecter les erreurs courantes Plugin Maven
Checkstyle Vérifie le style du code selon des règles prédéfinies Plugin Maven/Gradle
PMD Détecte les mauvaises pratiques et les répétitions Plugin Maven

De toutes façons, certains sont déjà présents dans les IDE comme IntelliJ et GitLab CI/CD.

Automatisation et intégration continue

Nous abordons ces outils dans le cours DevOps, mais je les liste pour info.

Outil Utilité Exemple
GitLab CI/CD Lancer les builds et tests automatiquement .gitlab-ci.yml
Docker Compose Lancer ton app + ta BDD en local docker-compose up
Maven Wrapper (mvnw) Exécuter Maven sans installation préalable ./mvnw test
Gradle Wrapper (gradlew) Idem pour Gradle ./gradlew test

Dépendances pour les tests

Exemple des dépendances ajoutées pour Spring Boot dans le fichier build.gradle ou pom.xml selon vos préférences.

Pour intégrer :

testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.mockito:mockito-core'
testImplementation 'org.assertj:assertj-core'
testImplementation 'org.testcontainers:junit-jupiter'
testImplementation 'org.testcontainers:postgresql'

Check-list final (Qualité et tests)

Domaine Outil ou pratique Vérifié
Tests unitaires JUnit 5 + Mockito
Tests d’intégration Web MockMvc / @WebMvcTest
Tests complets @SpringBootTest + TestRestTemplate
Base de test H2 ou Testcontainers
Couverture de code JaCoCo / SonarQube
Validation et erreurs testées @Valid + @ControllerAdvice
CI/CD GitLab avec pipeline Maven/Gradle