Bonjour ! Dans la leçon précédente, nous avons mis en place une couche d'autorisation robuste avec des rôles, nous permettant de définir précisément qui a le droit d'accéder à nos endpoints d'API. Notre application est maintenant fonctionnelle et sécurisée. Il est temps de garantir sa qualité sur le long terme.
Aujourd'hui, nous allons aborder une compétence fondamentale pour tout développeur professionnel : les tests d'intégration. L'objectif est d'apprendre à écrire des tests automatisés qui valident le bon fonctionnement de notre API de bout en bout, en s'assurant que toutes les couches (contrôleur, service, repository, base de données) collaborent comme prévu. Nous utiliserons pour cela les puissants outils fournis par Spring Boot Test.
1. Tests d'Intégration vs. Tests Unitaires
Avant de plonger dans le code, il est crucial de bien distinguer deux types de tests.
- Tests unitaires : Ils se concentrent sur une seule "unité" de code (une méthode, une classe) de manière isolée. Toutes les dépendances externes (comme la base de données ou d'autres services) sont "mockées", c'est-à-dire remplacées par des objets factices. L'objectif est de vérifier la logique interne d'un composant, indépendamment du reste du système.
- Tests d'intégration : Ils vérifient que plusieurs composants de l'application fonctionnent correctement ensemble. Au lieu de mocker les dépendances, on utilise des versions réelles ou quasi-réelles (par exemple, une vraie base de données qui tourne dans un conteneur). Ces tests sont plus lents mais beaucoup plus proches des conditions réelles d'utilisation.
L'analogie de la voiture est souvent utilisée : le test unitaire consiste à tester le moteur sur un banc d'essai, tandis que le test d'intégration consiste à démarrer la voiture pour vérifier que le moteur, la transmission et les roues fonctionnent ensemble.
Le diagramme ci-dessous illustre l'architecture d'un test d'intégration dans Spring Boot. On voit qu'un contexte d'application complet est chargé, avec un vrai serveur web et les différents Beans, simulant ainsi l'application réelle.

L'article suivant, bien que centré sur le "test slicing", propose une excellente introduction qui distingue les tests unitaires par couche (@WebMvcTest, @DataMongoTest) des tests d'intégration complets (@SpringBootTest).
Tests unitaires et d'intégration avec Spring Boot : le “test slicing”
Cet article d'Effective Coding clarifie les différentes stratégies de test dans Spring Boot.
Lisez l'introduction et les sections "Scénario 2 : Test unitaire brique DATA" et "Scénario 3 : Test d’intégration". Notez la différence fondamentale entre @DataMongoTest (qui ne charge que la couche de persistance) et @SpringBootTest (qui charge tout le contexte de l'application). C'est ce dernier que nous allons utiliser.
2. Mise en Place de l'Environnement de Test
Pour écrire nos tests d'intégration, nous devons configurer notre projet pour qu'il utilise un environnement de test isolé, notamment une base de données distincte de celle de production.
Étape 1 : Ajouter les dépendances Maven
Votre projet Spring Boot inclut déjà spring-boot-starter-test, qui contient JUnit 5, Mockito et les utilitaires de base. Nous allons ajouter une base de données en mémoire, H2, qui sera utilisée uniquement pendant les tests.
Ouvrez votre pom.xml et ajoutez cette dépendance :
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
Le scope test garantit que cette dépendance ne sera pas incluse dans le package final de votre application.
Étape 2 : Configurer une base de données de test
Créez un fichier de configuration spécifique pour les tests dans src/test/resources/application.properties. Spring Boot chargera automatiquement ce fichier lors de l'exécution des tests, surchargeant la configuration principale.
src/test/resources/application.properties
# Configuration de la base de données H2 en mémoire pour les tests
spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
# Configuration JPA pour H2
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=create-drop
jdbc:h2:mem:testdb: Crée une base de données en mémoire nomméetestdb.spring.jpa.hibernate.ddl-auto=create-drop: Demande à Hibernate de créer le schéma de la base de données au début des tests et de le détruire à la fin. Chaque exécution de test partira d'une base de données propre.
Étape 3 : La classe de test et ses annotations
Créons une classe de test pour notre UserController.
@SpringBootTest
@AutoConfigureMockMvc
public class UserControllerIntegrationTest {
// ... nos tests
}
@SpringBootTest: C'est l'annotation maîtresse. Elle demande à Spring Boot de charger le contexte complet de l'application, comme si elle démarrait normalement.@AutoConfigureMockMvc: ConfigureMockMvc, un outil puissant qui permet de simuler des requêtes HTTP vers nos contrôleurs sans passer par une vraie couche réseau, tout en exécutant la chaîne de traitement complète de Spring (filtres, intercepteurs, etc.).
3. Écrire notre Premier Test d'Intégration avec MockMvc
Nous allons maintenant écrire un test pour l'endpoint qui liste tous les utilisateurs (GET /api/users).
@SpringBootTest
@AutoConfigureMockMvc
public class UserControllerIntegrationTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private UserRepository userRepository;
@BeforeEach
void setUp() {
// Nettoyer la base avant chaque test
userRepository.deleteAll();
}
@Test
void shouldGetAllUsers() throws Exception {
// Arrange : préparer les données
User user1 = new User("user1", "user1@test.com", "password");
User user2 = new User("user2", "user2@test.com", "password");
userRepository.saveAll(List.of(user1, user2));
// Act & Assert : exécuter la requête et vérifier le résultat
mockMvc.perform(get("/api/users"))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.size()").value(2))
.andExpect(jsonPath("$[0].username").value("user1"));
}
}
La vidéo suivante explique très bien comment tester un contrôleur avec MockMvc. Même si elle utilise @WebMvcTest (un test de "slice"), les techniques avec MockMvc sont identiques à celles que nous utilisons dans notre test d'intégration complet.
Spring Boot Testing : Rédiger des Tests Unitaires en utilisant JUnit 5 - Mockito - Jacoco
Cette vidéo de Coding With Armand montre en détail l'utilisation de MockMvc pour tester un contrôleur.
Regardez attentivement la section sur le test du contrôleur (PersonController test). Concentrez-vous sur : L'injection de MockMvc. L'utilisation de mockMvc.perform() pour simuler les requêtes GET, POST, PUT. L'enchaînement des andExpect() pour vérifier le statut de la réponse (status().isOk()) et le contenu du corps JSON avec jsonPath().
Décortiquons notre test :
- Arrange : On prépare l'état de notre système. Ici, on insère deux utilisateurs dans notre base de données de test (H2).
- Act : On exécute l'action à tester avec
mockMvc.perform(get("/api/users")). - Assert : On vérifie que le résultat est correct à l'aide de
andExpect():status().isOk(): Le code de statut HTTP est 200.content().contentType(...): Le type de contenu de la réponse est bien du JSON.jsonPath("$.size()"):jsonPathest un langage d'expression pour interroger des documents JSON.$représente la racine du document. Ici, on vérifie que le tableau JSON retourné a une taille de 2.jsonPath("$[0].username"): On vérifie que le champusernamedu premier objet ([0]) du tableau est bien "user1".
4. Tester les Endpoints Sécurisés
Nos tests actuels vont probablement échouer si les endpoints sont protégés. En effet, MockMvc envoie par défaut des requêtes anonymes. Nous devons simuler un utilisateur authentifié. Pour cela, le module spring-security-test est indispensable.
Assurez-vous d'avoir cette dépendance dans votre pom.xml (avec la portée test) :
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
Nous pouvons maintenant utiliser l'annotation @WithMockUser sur nos méthodes de test.
Imaginons que notre endpoint GET /api/users soit réservé aux administrateurs, comme nous l'avons configuré dans la leçon précédente avec @PreAuthorize("hasRole('ADMIN')").
Test 1 : Accès non authentifié (doit échouer)
@Test
void shouldReturnForbiddenWhenGettingAllUsersWithoutAuth() throws Exception {
mockMvc.perform(get("/api/users"))
.andExpect(status().isForbidden()); // Ou isUnauthorized() selon la configuration
}
Test 2 : Accès avec le mauvais rôle (doit échouer)
@Test
@WithMockUser(roles = "USER")
void shouldReturnForbiddenWhenGettingAllUsersWithUserRole() throws Exception {
mockMvc.perform(get("/api/users"))
.andExpect(status().isForbidden());
}
Test 3 : Accès avec le bon rôle (doit réussir)
@Test
@WithMockUser(roles = "ADMIN")
void shouldReturnAllUsersWhenUserIsAdmin() throws Exception {
// ... même code que notre premier test (Arrange, Act, Assert)
mockMvc.perform(get("/api/users"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.size()").value(2));
}
Avec ces trois tests, nous avons une couverture complète du comportement de sécurité de notre endpoint. L'image ci-dessous montre à quoi ressemble une suite de tests d'intégration réussie dans IntelliJ.

Pour un exemple complet et très bien structuré d'une classe de test d'intégration utilisant les mêmes principes, vous pouvez consulter le code dans l'article suivant.
Tests d'intégration avec Springboot, Docker et Testcontainers
Cet article de Younup montre une implémentation professionnelle de tests d'intégration.
Examinez la classe de test finale (BookPostgresSQLTest). Elle combine @SpringBootTest, @AutoConfigureMockMvc, MockMvc et le nettoyage de la base de données avec @BeforeEach, appliquant exactement les concepts que nous venons de voir.
Conclusion
Félicitations ! Vous venez d'acquérir une compétence essentielle pour garantir la robustesse et la maintenabilité de vos applications backend. Les tests d'intégration vous donnent la confiance nécessaire pour faire évoluer votre code sans introduire de régressions.
Points clés à retenir :
- Les tests d'intégration valident la collaboration entre plusieurs composants, contrairement aux tests unitaires qui les isolent.
- L'annotation
@SpringBootTestest la clé pour lancer un contexte d'application complet. @AutoConfigureMockMvcet l'objetMockMvcpermettent de simuler des requêtes HTTP de manière simple et efficace.- Il est crucial d'utiliser une base de données de test (comme H2) et de la configurer dans
src/test/resources/application.properties. - L'expression
jsonPathest un outil puissant pour valider le contenu des réponses JSON. - Le module
spring-security-testet l'annotation@WithMockUserpermettent de tester facilement les endpoints sécurisés en simulant des utilisateurs avec des rôles spécifiques.
Dans la prochaine et dernière leçon de ce module, nous allons nous concentrer sur la documentation de notre API. Une API bien testée est une chose, mais une API bien documentée est essentielle pour qu'elle puisse être utilisée par d'autres développeurs ou des applications frontend. Nous explorerons des outils comme Postman ou Swagger pour générer une documentation claire et interactive.