Rejoignez-nous le 30 avril : dévoilement de Parasoft C/C++test CT pour l'excellence en matière de tests continus et de conformité | En savoir plus

Comment créer des tests paramétrés JUnit

Portrait de Brian McGlauflin,
19 septembre 2018
7 min lire

L'écriture de code de test paramétré peut représenter beaucoup de travail car chaque test nécessite une grande partie du code correctement écrit. Heureusement, voici comment vous pouvez utiliser Parasoft Jtest pour générer automatiquement vos tests paramétrés sans écrire de codes passe-partout.

Les tests paramétrés sont un bon moyen de définir et d'exécuter plusieurs cas de test, où la seule différence entre eux réside dans les données. Ici, nous examinons trois frameworks différents couramment utilisés avec les tests JUnit.

Lors de l'écriture de tests unitaires, il est courant d'initialiser les paramètres d'entrée de la méthode et les résultats attendus dans la méthode de test elle-même. Dans certains cas, l'utilisation d'un petit ensemble d'entrées suffit; cependant, il existe des cas dans lesquels nous devons utiliser un grand ensemble de valeurs pour vérifier toutes les fonctionnalités de notre code. Les tests paramétrés sont un bon moyen de définir et d'exécuter plusieurs cas de test, où la seule différence entre eux réside dans les données. Ils peuvent valider le comportement du code pour une variété de valeurs, y compris les cas de bordure. Le paramétrage des tests peut augmenter la couverture du code et garantir que le code fonctionne comme prévu.

Il existe un certain nombre de bons cadres de paramétrage pour Java. Dans cet article, nous examinerons trois frameworks différents couramment utilisés avec les tests JUnit, avec une comparaison entre eux et des exemples de la façon dont les tests sont structurés pour chacun. Enfin, nous explorerons comment simplifier et accélérer la création de tests paramétrés.

Cadres de test paramétrés JUnit

Comparons les 3 frameworks les plus courants: JUnit 4, JunitParams et JUnit 5. Chaque framework de paramétrage JUnit a ses propres forces et faiblesses.

Unité 4

Avantages:

  • Il s'agit du cadre de paramétrage intégré à JUnit 4, il ne nécessite donc aucune dépendance externe supplémentaire.
  • Il prend en charge les anciennes versions de Java (JDK 7 et antérieures).

Inconvénients:

  • Les classes de test utilisent des champs et des constructeurs pour définir des paramètres, ce qui rend les tests plus détaillés.
  • Il nécessite une classe de test distincte pour chaque méthode testée.

JunitParams

Avantages:

  • Simplifie la syntaxe des paramètres en autorisant la transmission directe des paramètres à une méthode de test.
  • Permet plusieurs méthodes de test (chacune avec leurs propres données) par classe de test.
  • Prend en charge les sources de données CSV, ainsi que les valeurs basées sur les annotations (aucune méthode requise).

Inconvénients:

  • Nécessite que le projet soit configuré avec la dépendance JunitParams.
  • Lors de l'exécution et du débogage des tests, tous les tests de la classe doivent être exécutés - il n'est pas possible d'exécuter une seule méthode de test dans une classe de test.

Unité 5

Avantages:

  • Ce cadre de paramétrage est intégré à JUnit 5 et améliore ce qui était inclus avec JUnit 4.
  • A une syntaxe de paramètre simplifiée comme JunitParams.
  • Prend en charge plusieurs types de sources d'ensembles de données, y compris CSV et annotation (aucune méthode requise).
  • Même si aucune dépendance supplémentaire n'est requise, plusieurs fichiers .jar sont nécessaires.

Inconvénients:

  • Nécessite Java 8 et une version plus récente du système de construction (Gradle 4.6 ou Maven Surefire 2.21).
  • Peut ne pas être encore pris en charge dans votre IDE (au moment de la rédaction de cet article, seuls Eclipse et IntelliJ prennent en charge JUnit 5).

Exemple

À titre d'exemple, supposons que nous ayons une méthode qui traite les demandes de prêt pour une banque. Nous pourrions écrire un test unitaire qui spécifie le montant de la demande de prêt, le montant de l'acompte et d'autres valeurs. Nous créerions alors des assertions qui valident la réponse - le prêt peut être approuvé ou rejeté, et la réponse peut spécifier les conditions du prêt.

Par exemple :

public LoanResponse requestLoan (float prêtAmount, float downPayment, float availableFunds) {LoanResponse response = new LoanResponse (); response.setApproved (vrai); if (availableFunds <downPayment) {response.setApproved (false); response.setMessage ("error.insufficient.funds.for.down.payment"); réponse de retour; } if (downPayment / prêtAmount <0.1) {response.setApproved (false); response.setMessage ("error.insufficient.down.payment"); } réponse de retour; }

Voir Raw

exemple_test_paramètre_1.java

Animé par Paul Allard et Axel Bonaldo GitHub

Tout d'abord, regardons un test régulier pour la méthode ci-dessus:

@Test public void testRequestLoan () jette Throwable {// Donné LoanProcessor underTest = new LoanProcessor (); // Lorsque LoanResponse result = underTest.requestLoan (1000f, 200f, 250f); // Puis assertNotNull (résultat); assertTrue (result.isApproved ()); assertNull (result.getMessage ()); }

Voir Raw

exemple_test_paramètre_2.java

Animé par Paul Allard et Axel Bonaldo GitHub

Dans cet exemple, nous testons notre méthode en demandant un prêt de 1000 200 $, avec un acompte de 250 $ et en indiquant que le demandeur dispose de XNUMX $ de fonds disponibles. Le test valide ensuite que le prêt a été approuvé et n'a pas fourni de message dans la réponse.

Afin de nous assurer que notre requestLoan () méthode est testée à fond, nous devons tester avec une variété d'acomptes, les montants de prêt demandés et les fonds disponibles. Par exemple, testons une demande de prêt d'un million de dollars sans acompte, qui devrait être rejetée. Nous pourrions simplement dupliquer le test existant avec des valeurs différentes, mais comme la logique de test serait la même, il est plus efficace de paramétrer le test à la place.

Nous paramétrerons le montant du prêt demandé, l'acompte et les fonds disponibles, ainsi que les résultats attendus: si le prêt a été approuvé, et le message renvoyé après validation. Chaque ensemble de données de demande, ainsi que ses résultats attendus, deviendra son propre scénario de test.

Un exemple de test paramétré utilisant JUnit 4 paramétré

Commençons par un exemple Junit 4 Parameterized. Pour créer un test paramétré, nous devons d'abord définir les variables du test. Nous devons également inclure un constructeur pour les initialiser:

@RunWith (Parameterized.class) classe publique LoanProcessorParameterizedTest {float prêtAmount; float downPayment; float availableFunds; boolean expectApproved; String expectedMessage; public LoanProcessorParameterizedTest (float prêtAmount, float downPayment, float availableFunds, booléen expectApproved, String expectedMessage) {this.loanAmount = prêtAmount; this.downPayment = downPayment; this.availableFunds = availableFunds; this.expectApproved = expectApproved; this.expectedMessage = expectedMessage; } // ...}

Voir Raw

exemple_test_paramètre_3.java

Animé par Paul Allard et Axel Bonaldo GitHub

Ici, on voit que le test utilise le @Courir avec annotation pour spécifier que le test s'exécutera avec le runner paramétré Junit4. Ce coureur sait rechercher une méthode qui fournira l'ensemble de valeurs pour le test (annoté avec @Paramètres), initialisez correctement le test et exécutez les tests avec plusieurs lignes.

Notez que chaque paramètre est défini comme un champ dans la classe de test et que le constructeur initialise ces valeurs (vous pouvez également injecter des valeurs dans les champs à l'aide du @Paramètre annotation si vous ne souhaitez pas créer de constructeur). Pour chaque ligne de l'ensemble de valeurs, le Paramétré runner instanciera la classe de test et exécutera chaque test de la classe.

Ajoutons une méthode qui fournit les paramètres au Paramétré coureur:

@Parameters (name = "Run {index}: prêtAmount = {0}, downPayment = {1}, availableFunds = {2}, expectApproved = {3}, expectedMessage = {4}") public static Iterable data () jette Throwable {return Arrays.asList (new Object [] [] {{1000.0f, 200.0f, 250.0f, true, null}}); }

Voir Raw

exemple_test_paramètre_4.java

Animé par Paul Allard et Axel Bonaldo GitHub

Les ensembles de valeurs sont construits comme un Liste of Objet tableaux par le Les données() méthode, qui est annotée avec @Paramètres. Noter que @Paramètres définit le nom du test à l'aide d'espaces réservés, qui seront remplacés lors de l'exécution du test. Cela facilite la visualisation des valeurs dans les résultats des tests, comme nous le verrons plus tard. Actuellement, il n'y a qu'une seule ligne de données, testant un cas où le prêt devrait être approuvé. Nous pouvons ajouter plus de lignes pour augmenter la couverture de la méthode testée.

@Parameters (name = "Run {index}: prêtAmount = {0}, downPayment = {1}, availableFunds = {2}, expectApproved = {3}, expectedMessage = {4}") public static Iterable data () jette Throwable {return Arrays.asList (new Object [] [] {{1000.0f, 200.0f, 250.0f, true, null}, {1000.0f, 50.0f, 250.0f, false, "error.insufficient. down.payment "}, {1000.0f, 200.0f, 150.0f, false," error.insufficient.funds.for.down.payment "}}); }

Voir Raw

exemple_test_paramètre_5.java

Animé par Paul Allard et Axel Bonaldo GitHub

Ici, nous avons un cas de test où le prêt serait approuvé, et deux cas dans lesquels il ne devrait pas être approuvé pour des raisons différentes. Nous pouvons souhaiter ajouter des lignes dans lesquelles des valeurs nulles ou négatives sont utilisées, ainsi que des conditions aux limites de test.

Nous sommes maintenant prêts à créer la méthode de test:

@Test public void testRequestLoan () jette Throwable {// Donné LoanProcessor underTest = new LoanProcessor (); // When LoanResponse result = underTest.requestLoan (prêtAmount, downPayment, availableFunds); // Puis assertNotNull (résultat); assertEquals (expectApproved, result.isApproved ()); assertEquals (expectedMessage, result.getMessage ()); }

Voir Raw

exemple_test_paramètre_6.java

Animé par Paul Allard et Axel Bonaldo GitHub

Ici, nous référençons les champs lors de l'appel de la requestLoan () méthode et valider les résultats.

Exemple JunitParams

La bibliothèque JunitParams simplifie la syntaxe de test paramétrée en permettant aux paramètres d'être transmis directement à la méthode de test. Les valeurs des paramètres sont fournies par une méthode distincte dont le nom est référencé dans le @Paramètres annotation.

@RunWith (JUnitParamsRunner.class) classe publique LoanProcessorParameterizedTest2 {@Test @Parameters (method = "testRequestLoan_Parameters") public void testRequestLoan (float LoanProcessorParameterizedTest0 {@Test @Parameters (method = "testRequestLoan_Parameters") public void testRequestLoan (float LoanProcessorParameterizedTest1 {@Test @Parameter) ("non utilisé") Objet statique privé [] [] testRequestLoan_Parameters () jette Throwable {// Paramètres: prêtAmount = {2}, downPayment = {3}, availableFunds = {4}, expectApproved = {1000.0}, expectedMessage = {200.0 } retourne un nouvel objet [] [] {{250.0f, 1000.0f, 50.0f, true, null}, {250.0f, 1000.0f, 200.0f, false, "error.insufficient.down.payment"}, {150.0f , XNUMXf, XNUMXf, false, "error.insufficient.funds.for.down.payment"}}; }}

Voir Raw

exemple_test_paramètre_7.java

Animé par Paul Allard et Axel Bonaldo GitHub

JunitParams présente l'avantage supplémentaire de prendre en charge l'utilisation de fichiers CSV pour fournir des valeurs en plus de fournir les valeurs dans le code. Cela permet au test d'être découplé des données et des valeurs de données à mettre à jour sans mettre à jour le code.

Exemple Junit 5

JUnit 5 résout certaines des limitations et des lacunes de JUnit 4. Comme JunitParams, Junit 5 simplifie également la syntaxe des tests paramétrés. Les changements de syntaxe les plus importants sont:

  • La méthode de test est annotée avec @Test Paramétré au lieu de @Tester
  • La méthode de test accepte les paramètres directement, au lieu d'utiliser des champs et un constructeur
  • La @Courir avec l'annotation n'est plus nécessaire

La définition du même exemple dans Junit 5 ressemblerait à ceci:

public class LoanProcessorParameterizedTest {@ParameterizedTest (name = "Run {index}: prêtAmount = {0}, downPayment = {1}, availableFunds = {2}, expectApproved = {3}, expectedMessage = {4}") @MethodSource (" testRequestLoan_Parameters ") public void testRequestLoan (float prêtAmount, float downPayment, float availableFunds, boolean expectApproved, String expectedMessage) jette Throwable {...} flux statique testRequestLoan_Parameters () jette Throwable {retour Stream.of (Arguments.of (1000.0f, 200.0f, 250.0f, true, null), Arguments.of (1000.0f, 50.0f, 250.0f, false, "error.insufficient.down .payment "), Arguments.of (1000.0f, 200.0f, 150.0f, false," error.insufficient.funds.for.down.payment ")); }}

Voir Raw

exemple_test_paramètre_8.java

Animé par Paul Allard et Axel Bonaldo GitHub

Créez efficacement des tests paramétrés

Comme on peut l'imaginer, écrire le test paramétré ci-dessus peut être un peu de travail. Pour chaque cadre de test paramétré, il y a un peu de code standard qui doit être écrit correctement. Il peut être difficile de se souvenir de la structure correcte et la rédaction prend du temps. Pour rendre cela beaucoup plus facile, vous pouvez utiliser Jtest Parasoft pour générer des tests paramétrés, automatiquement, comme ceux décrits ci-dessus. Pour ce faire, sélectionnez simplement la méthode pour laquelle vous souhaitez générer un test (dans Eclipse ou IntelliJ),:

Le test est généré à l'aide des valeurs et des assertions par défaut. Vous pouvez ensuite configurer le test avec des valeurs d'entrée et des assertions réelles, et ajouter plus de lignes de données à la méthode data ().

Exécution du test paramétré

Parasoft Jtest peut exécuter des tests paramétrés directement dans Eclipse et IntelliJ.

La vue JUnit dans Eclipse

Notez que le nom de chaque test, comme indiqué, inclut les valeurs d'entrée de l'ensemble de données et les valeurs de résultat attendues. Cela peut faciliter le débogage du test en cas d'échec, car les paramètres d'entrée et les sorties attendues sont affichés pour chaque cas.

Vous pouvez également utiliser l'action Exécuter tout de Parasoft Jtest:

La vue Flow Tree dans Parasoft Jtest

Il analyse le flux de test et fournit des informations détaillées sur le test précédent. Cela vous permet de voir ce qui s'est passé dans le test sans avoir besoin de réexécuter le test avec des points d'arrêt ou des instructions de débogage. Par exemple, vous pouvez voir les valeurs paramétrées dans la vue Variables:

La vue des variables dans Parasoft Jtest

Conclusion

Chacun des trois cadres que nous avons examinés est un bon choix et fonctionne bien. Si j'utilise JUnit 4, je préfère JunitParams au framework paramétré JUnit 4 intégré, en raison de la conception plus propre des classes de test et de la possibilité de définir plusieurs méthodes de test dans la même classe. Cependant, si vous utilisez JUnit 5, je recommanderais le framework JUnit 5 intégré car il corrige les lacunes de JUnit 4 et ne nécessite aucune bibliothèque supplémentaire. j'aime aussi utiliser Les tests unitaires de Parasoft capacités pour rendre la création, l'exécution et le débogage de tests paramétrés plus efficaces.

Découvrez comment Parasoft Jtest peut vous aider à améliorer la qualité de votre code Java et la productivité de votre équipe.