Test en Java : des tests unitaires aux tests de bout en bout
Des tests de qualité sont la base d'un logiciel fiable. Dans le monde Java, il existe une pyramide de test bien établie qui comprend trois niveaux principaux : unit (tests unitaires), integration (tests d'intégration) et e2e (tests de bout en bout). Dans cet article, nous allons analyser chacun d'eux, fournir des exemples pratiques en Java et expliquer comment construire une stratégie de test efficace dans votre projet.
1. Tests unitaires : la base de la pyramide
Les tests unitaires vérifient des composants individuels du programme en isolation — généralement une classe ou une méthode. Ils s'exécutent rapidement, ne nécessitent pas de dépendances externes (BD, API) et doivent couvrir la majeure partie de la logique métier.
Outils pour les tests unitaires en Java
- JUnit 5 — le framework standard pour écrire des tests.
- Mockito — une bibliothèque pour créer des objets mock.
- AssertJ — des assertions fluides pour des vérifications lisibles.
Exemple de test unitaire avec JUnit 5 et Mockito
Supposons que nous ayons un service OrderService qui dépend de PaymentGateway. Nous voulons tester la logique de calcul de remise sans appeler la passerelle de paiement réelle.
// Classe à testerpublic class OrderService { private final PaymentGateway paymentGateway;
public OrderService(PaymentGateway paymentGateway) { this.paymentGateway = paymentGateway; }
public double calculateDiscount(double amount) { if (amount > 1000) { return amount * 0.1; // 10% de remise } return 0; }}
// Test unitaireimport org.junit.jupiter.api.Test;import org.junit.jupiter.api.extension.ExtendWith;import org.mockito.InjectMocks;import org.mockito.Mock;import org.mockito.junit.jupiter.MockitoExtension;import static org.junit.jupiter.api.Assertions.assertEquals;
@ExtendWith(MockitoExtension.class)public class OrderServiceTest {
@Mock private PaymentGateway paymentGateway;
@InjectMocks private OrderService orderService;
@Test public void testCalculateDiscount_AmountAbove1000_Returns10Percent() { double result = orderService.calculateDiscount(1500); assertEquals(150.0, result, 0.001); }
@Test public void testCalculateDiscount_AmountBelow1000_ReturnsZero() { double result = orderService.calculateDiscount(500); assertEquals(0.0, result); }}Bonnes pratiques : utilisez @BeforeEach pour initialiser les données communes, évitez de tester directement les méthodes privées, suivez la règle "une vérification par test".
2. Tests d'intégration : vérification des interactions
Les tests d'intégration vérifient comment plusieurs modules fonctionnent ensemble — par exemple, l'interaction d'un service avec une base de données, des files d'attente de messages ou des API externes. Contrairement aux tests unitaires, on utilise ici des dépendances réelles ou in-memory.
Outils pour les tests d'intégration
- Spring Boot Test — pour tester les applications Spring.
- Testcontainers — pour lancer des conteneurs Docker (BD, Kafka).
- H2 Database — une base in-memory pour des tests rapides.
Exemple de test d'intégration avec Spring Boot
Nous testons un contrôleur REST avec une connexion réelle à la BD (dans le test, nous utilisons H2).
// Contrôleur@RestController@RequestMapping("/api/users")public class UserController { private final UserRepository userRepository;
public UserController(UserRepository userRepository) { this.userRepository = userRepository; }
@GetMapping("/{id}") public User getUser(@PathVariable Long id) { return userRepository.findById(id) .orElseThrow(() -> new Runti