paint-brush
Tests en Java : les concepts clés [Partie 1 : Tests unitaires]par@gromspys
1,418 lectures
1,418 lectures

Tests en Java : les concepts clés [Partie 1 : Tests unitaires]

par Sergei Korneev10m2023/11/14
Read on Terminal Reader

Trop long; Pour lire

Dans cet article, nous explorerons les concepts clés des tests unitaires Java, notamment des exemples de code simples, le paramétrage, les tests d'exception et les annotations.
featured image - Tests en Java : les concepts clés [Partie 1 : Tests unitaires]
Sergei Korneev HackerNoon profile picture

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 @Before , @BeforeEach , @After et @AfterEach , ainsi que l'utilisation de simulacres et de stubs. .


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 Calculator contient des opérations arithmétiques de base :

 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 Calculator , vous pouvez utiliser le framework JUnit, un framework de test populaire pour Java. Écrivons un test unitaire simple pour la méthode 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 @Test . Nous créons ensuite une instance de la classe Calculator et affirmons que le résultat de la méthode add est égal à la valeur attendue (5).

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 @ParameterizedTest et fournir les valeurs d'entrée et les résultats attendus en tant que paramètres. Voici un exemple :

 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 @ParameterizedTest 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 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 @Test avec la méthode assertThrows fournie par JUnit.


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 Calculator lève une ArithmeticException .

Utilisation des annotations : @Before, @BeforeEach, @After et @AfterEach

Des annotations telles que @Before , @BeforeEach , @After et @AfterEach 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 et @After s'exécutent respectivement une fois avant et après toutes les méthodes de test de la classe de test.
  • @BeforeEach et @AfterEach s'exécutent avant et après chaque méthode de test.


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 setUp est annotée avec @BeforeEach et initialise l'objet Calculator avant chaque test. La méthode tearDown , annotée avec @AfterEach , nettoie les ressources après chaque test.

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.


  • Mock : 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.


  • Stub : 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.


Développons la classe Calculator pour inclure une méthode qui implique une dépendance externe, puis créons un stub pour simuler cette dépendance.

 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 Calculator mise à jour, performComplexCalculation s'appuie sur un ExternalService pour la multiplication et l'obtention d'une valeur constante.


Voici comment créer un stub pour tester la classe Calculator sans dépendre du ExternalService réel.

 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, ExternalService 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 Calculator s’exécute sans dépendre de l’implémentation réelle de 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 PaymentService qui interagit avec une passerelle de paiement externe. Nous pouvons utiliser Mockito pour créer une simulation pour la passerelle de paiement :

 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 PaymentGateway fictif et utilisons la méthode when pour définir son comportement. Nous appelons ensuite la méthode processPayment sur la classe PaymentService et vérifions que la méthode charge a été appelée avec les paramètres attendus.

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 :


  1. Ouvrez le fichier pom.xml de votre projet.
  2. 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 target/site/jacoco/index.html . Ce rapport fournit des informations détaillées sur la couverture de votre code par vos tests unitaires.

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 !