Les tests unitaires sont une pratique fondamentale dans le développement de logiciels qui garantit la fiabilité et l'exactitude de votre code. Dans cet article, nous explorerons les concepts clés des tests unitaires Java, y compris des exemples de code simples, le paramétrage, les tests d'exceptions, les annotations telles que , , et , ainsi que l'utilisation de simulacres et de stubs. . @Before @BeforeEach @After @AfterEach Nous présenterons également la populaire bibliothèque Mockito pour se moquer et fournirons des conseils sur la mesure de la couverture de code à l'aide des plugins Maven. L'importance des tests unitaires Les tests unitaires consistent à tester des composants individuels ou des unités de code de manière isolée pour vérifier leur exactitude. L'objectif principal est de détecter et de corriger les bogues dès le début du processus de développement, en garantissant que chaque unité de code se comporte comme prévu. Commençons par créer une classe de calculatrice simple que nous pouvons utiliser pour nos exemples. La classe contient des opérations arithmétiques de base : Calculator public class Calculator { public int add(int a, int b) { return a + b; } public int subtract(int a, int b) { return a - b; } public int multiply(int a, int b) { return a * b; } public int divide(int a, int b) { return a / b; } } Écrire un test unitaire simple Pour créer un test unitaire pour notre classe , vous pouvez utiliser le framework JUnit, un framework de test populaire pour Java. Écrivons un test unitaire simple pour la méthode : Calculator add import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; public class CalculatorTest { @Test public void testAdd() { Calculator calculator = new Calculator(); int result = calculator.add(2, 3); assertEquals(5, result); } } Dans ce test, nous importons les classes JUnit nécessaires et annotons la méthode de test avec . Nous créons ensuite une instance de la classe et affirmons que le résultat de la méthode est égal à la valeur attendue (5). @Test Calculator add Tests paramétrés Les tests paramétrés vous permettent d'exécuter la même logique de test avec plusieurs ensembles de données d'entrée. Ceci est utile pour tester une méthode avec différentes valeurs d’entrée. Pour ce faire dans JUnit, vous pouvez utiliser l'annotation et fournir les valeurs d'entrée et les résultats attendus en tant que paramètres. Voici un exemple : @ParameterizedTest import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import static org.junit.jupiter.api.Assertions.assertEquals; public class CalculatorTest { @ParameterizedTest @CsvSource({"2, 3, 5", "4, 7, 11", "0, 0, 0"}) public void testAdd(int a, int b, int expected) { Calculator calculator = new Calculator(); int result = calculator.add(a, b); assertEquals(expected, result); } } Dans cet exemple, l'annotation nous permet de fournir plusieurs ensembles de valeurs d'entrée et de résultats attendus au format CSV. La méthode de test est exécutée pour chaque combinaison, garantissant l'exactitude de la méthode . @ParameterizedTest add Tests d'exceptions Tester les exceptions est crucial pour garantir que votre code gère les erreurs de manière appropriée. Pour tester les cas d'exception, vous pouvez utiliser l'annotation avec la méthode fournie par JUnit. @Test assertThrows Voici un exemple : javaCopy codeimport org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertThrows; public class CalculatorTest { @Test public void testDivideByZero() { Calculator calculator = new Calculator(); assertThrows(ArithmeticException.class, () -> calculator.divide(5, 0)); } } Dans ce test, nous vérifions que la division d'un nombre par zéro dans la classe lève une . Calculator ArithmeticException Utilisation des annotations : @Before, @BeforeEach, @After et @AfterEach Des annotations telles que , , et sont utilisées pour configurer et supprimer l'environnement de test. Ces annotations aident à gérer les tâches courantes d'initialisation et de nettoyage des tests. @Before @BeforeEach @After @AfterEach et s'exécutent respectivement une fois avant et après toutes les méthodes de test de la classe de test. @Before @After et s'exécutent avant et après chaque méthode de test. @BeforeEach @AfterEach Voici un exemple : import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; public class CalculatorTest { private Calculator calculator; @BeforeEach public void setUp() { calculator = new Calculator(); } @AfterEach public void tearDown() { calculator = null; } @Test public void testAdd() { int result = calculator.add(2, 3); assertEquals(5, result); } @Test public void testSubtract() { int result = calculator.subtract(5, 3); assertEquals(2, result); } } Dans cet exemple, la méthode est annotée avec et initialise l'objet avant chaque test. La méthode , annotée avec , nettoie les ressources après chaque test. setUp @BeforeEach Calculator tearDown @AfterEach Mocks et talons Les simulations et les stubs sont essentiels dans les tests unitaires lorsque vous souhaitez isoler le code testé des dépendances externes, telles que les bases de données ou les services Web. Ils permettent de simuler le comportement de ces dépendances. un simulacre est un objet qui imite le comportement d'un objet réel mais qui est sous votre contrôle. Il enregistre les interactions et permet de vérifier que des méthodes spécifiques sont appelées. Mock : un stub fournit des réponses prédéfinies aux appels de méthode. Il renvoie des données prédéfinies et est utilisé pour simuler certains comportements d'un système externe. Stub : Développons la classe pour inclure une méthode qui implique une dépendance externe, puis créons un stub pour simuler cette dépendance. Calculator public class Calculator { private ExternalService externalService; public Calculator(ExternalService externalService) { this.externalService = externalService; } public int performComplexCalculation(int a, int b) { int result = externalService.multiply(a, b); return result + externalService.getConstant(); } } Dans cette classe mise à jour, s'appuie sur un pour la multiplication et l'obtention d'une valeur constante. Calculator performComplexCalculation ExternalService Voici comment créer un stub pour tester la classe sans dépendre du réel. Calculator ExternalService import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.*; public class CalculatorTest { @Test public void testPerformComplexCalculation() { // Create a stub for ExternalService ExternalService externalServiceStub = new ExternalService() { @Override public int multiply(int a, int b) { return a * b; } @Override public int getConstant() { return 5; // Stubbed constant value } }; // Create the Calculator with the stubbed ExternalService Calculator calculator = new Calculator(externalServiceStub); // Test the performComplexCalculation method int result = calculator.performComplexCalculation(2, 3); assertEquals(11, result); // 2*3 + 5 (constant) = 11 } } Dans ce test, est remplacé dans la méthode de test en créant une classe anonyme qui remplace les méthodes nécessaires. De cette façon, la méthode de test s’exécute sans dépendre de l’implémentation réelle de . ExternalService Calculator ExternalService Les stubs sont utiles pour simuler le comportement de systèmes ou de dépendances externes afin d'isoler et de tester la fonctionnalité spécifique d'une classe ou d'une méthode. Cela vous permet de contrôler le comportement du stub et de vous concentrer sur l'unité testée sans avoir besoin de services externes réels. Présentation de Mockito pour se moquer Mockito est une bibliothèque Java populaire pour créer et gérer des objets fictifs. Disons que nous avons une classe qui interagit avec une passerelle de paiement externe. Nous pouvons utiliser Mockito pour créer une simulation pour la passerelle de paiement : PaymentService import org.junit.jupiter.api.Test; import static org.mockito.Mockito.*; public class PaymentServiceTest { @Test public void testProcessPayment() { PaymentGateway paymentGateway = mock(PaymentGateway.class); PaymentService paymentService = new PaymentService(paymentGateway); when(paymentGateway.charge(100.0)).thenReturn(true); boolean result = paymentService.processPayment(100.0); assertTrue(result); verify(paymentGateway, times(1)).charge(100.0); } } Dans cet exemple, nous créons un fictif et utilisons la méthode pour définir son comportement. Nous appelons ensuite la méthode sur la classe et vérifions que la méthode a été appelée avec les paramètres attendus. PaymentGateway when processPayment PaymentService charge Couverture du code et plugin Maven La couverture de code mesure le pourcentage de lignes de code, de branches ou d'instructions exécutées par vos tests unitaires. Il vous aide à identifier le code non testé et les zones pouvant nécessiter des tests supplémentaires. Maven est un outil de construction populaire pour les projets Java, et vous pouvez intégrer l'analyse de couverture de code dans votre projet Maven à l'aide de plugins comme JaCoCo. Voici comment ajouter JaCoCo à votre projet : Ouvrez le fichier de votre projet. pom.xml Ajoutez la configuration du plugin JaCoCo Maven : <build> <plugins> <plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>0.8.7</version> <executions> <execution> <goals> <goal>prepare-agent</goal> </goals> </execution> <execution> <id>report</id> <phase>test</phase> <goals> <goal>report</goal> </goals> </execution> </executions> </plugin> </plugins> </build> Exécutez votre build Maven avec la commande suivante : mvn clean verify Après avoir exécuté la build, vous pouvez trouver le rapport de couverture de code dans le fichier . Ce rapport fournit des informations détaillées sur la couverture de votre code par vos tests unitaires. target/site/jacoco/index.html Conclusion Les tests unitaires Java sont une pratique essentielle pour garantir la fiabilité et l'exactitude de votre code. Avec des outils comme JUnit et Mockito, vous pouvez écrire des tests unitaires et des simulations efficaces pour vos composants. En intégrant l'analyse de couverture de code à Maven et JaCoCo, vous pouvez vous assurer que vos tests couvrent une partie importante de votre base de code. Tester et maintenir régulièrement des tests unitaires vous aidera à produire des applications Java robustes et de haute qualité. Dans la prochaine partie de cette série, nous explorerons les tests d'intégration, un aspect critique des tests logiciels qui implique de tester les interactions entre différents composants et services pour garantir le bon fonctionnement global du système. Restez à l'écoute pour la deuxième partie, où nous plongerons dans le monde passionnant des tests d'intégration !