Introduction : Pourquoi les tests sont-ils nécessaires en Java ?
\nChaque développeur Java a déjà été confronté à une situation où, après avoir modifié une seule ligne de code, tout le système « cassait ». Pour éviter de telles catastrophes, l'industrie a développé un système de test structuré. Dans cet article, nous aborderons trois niveaux de tests en Java : unit (tests unitaires), integration (tests d'intégration) et e2e (tests de bout en bout). Vous découvrirez leurs différences, les outils à utiliser et comment écrire correctement des tests en Java.
\nQue vous écriviez des microservices avec Spring Boot ou des applications de bureau, comprendre la pyramide des tests est une compétence indispensable. C'est parti !
\n\n1. Tests unitaires : Le fondement de la qualité
\nLes tests unitaires vérifient le fonctionnement de classes ou de méthodes individuelles, isolées de leurs dépendances externes. C'est le niveau de test le plus rapide et le moins coûteux.
\n\n1.1 Outils principaux
\n- \n
- JUnit 5 — le framework standard pour Java (dernière version : Jupiter). \n
- Mockito — une bibliothèque pour créer des objets mock (simulateurs). \n
- AssertJ — une bibliothèque avec un riche ensemble d'assertions fluides. \n
1.2 Exemple de test unitaire avec JUnit 5 et Mockito
\nSupposons que nous ayons un service UserService qui dépend d'un repository UserRepository :
public class UserService {\n private final UserRepository repository;\n\n public UserService(UserRepository repository) {\n this.repository = repository;\n }\n\n public String getUserName(Long id) {\n User user = repository.findById(id);\n return user != null ? user.getName() : "Unknown";\n }\n}\nÉcrivons un test unitaire en isolant la dépendance externe via Mockito :
\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.mockito.Mockito.when;\n\n@ExtendWith(MockitoExtension.class)\nclass UserServiceTest {\n\n @Mock\n private UserRepository userRepository;\n\n @InjectMocks\n private UserService userService;\n\n @Test\n void testGetUserName_WhenUserExists() {\n // Arrange\n User mockUser = new User(1L, "Alice");\n when(userRepository.findById(1L)).thenReturn(mockUser);\n\n // Act\n String result = userService.getUserName(1L);\n\n // Assert\n assertEquals("Alice", result);\n }\n\n @Test\n void testGetUserName_WhenUserNotFound() {\n when(userRepository.findById(99L)).thenReturn(null);\n\n String result = userService.getUserName(99L);\n\n assertEquals("Unknown", result);\n }\n}\nRègle de bonne pratique : un test = une vérification. Utilisez des noms de méthodes explicites (par exemple, testGetUserName_WhenUserExists).
\n\n2. Tests d'intégration : Vérification des interactions
\nLes tests d'intégration vérifient comment les composants du système fonctionnent ensemble : base de données, système de fichiers, API externes. Ils sont plus lents que les tests unitaires, mais offrent une plus grande confiance dans la correction des liaisons.
\n\n2.1 Outils pour les tests d'intégration Java
\n- \n
- Spring Boot Test —
@SpringBootTestpour démarrer le contexte de l'application. \n - Testcontainers — lancement de bases de données réelles (PostgreSQL, MySQL) dans des conteneurs Docker. \n
- H2 — base de données en mémoire pour des tests rapides. \n
2.2 Exemple de test d'intégration avec Spring Boot et Testcontainers
\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.t