Découvrez GoogleTest certifié TÜV avec Agentic AI pour les tests C/C++ !
Plus de détails »
Blog Parasoft
En simplifiant la gestion des mocks, Parasoft Jtest vous permet d'écrire rapidement et facilement des tests unitaires. Découvrez comment automatiser un test unitaire Java , notamment grâce aux mocks et aux assertions.
Aller à la section
De bons tests unitaires sont essentiels pour garantir le bon fonctionnement de votre code, aujourd'hui comme demain. Une suite complète de tests, avec une couverture de code et de comportement adéquate, peut faire gagner un temps précieux et éviter bien des tracas à une organisation. Pourtant, il n'est pas rare de voir des projets où les tests sont insuffisants. Certains développeurs vont même jusqu'à s'opposer à leur utilisation.
Il existe de nombreuses raisons pour lesquelles les développeurs n'écrivent pas suffisamment de tests unitaires. L'une des principales raisons est le temps nécessaire à la construction et à la maintenance, en particulier dans les grands projets complexes. Dans les projets complexes, un test unitaire doit souvent instancier et configurer de nombreux objets. Cela prend beaucoup de temps à mettre en place et peut rendre le test aussi complexe (ou plus complexe) que le code qu'il teste lui-même.
Regardons un exemple en Java:
{LoanResponse response = new LoanResponse();response.setApproved(true);if (loanRequest.getDownPayment().compareTo(loanRequest.getAvailableFunds()) > 0) {response.setApproved(false);response.setMessage("error.insufficient.funds.for.down.payment");return response;}if (strategy.getQualifier(loanRequest) < strategy.getThreshold(adminManager)) {response.setApproved(false);response.setMessage(getErrorMessage());}return response;}
Voici une méthode qui traite une demande de prêt (LoanRequest) et génère une réponse (LoanResponse ). Notez l' argument LoanStrategy , utilisé pour traiter la demande de prêt . L'objet stratégie peut être complexe : il peut accéder à une base de données, à un système externe ou lever une exception RuntimeException . Pour écrire un test pour requestLoan() , je dois déterminer le type de LoanStrategy utilisé et je devrai probablement tester ma méthode avec différentes implémentations de LoanStrategy et configurations de demandes de prêt .
Un test unitaire pour requestLoan() pourrait ressembler à ceci :
@Testpublic void testRequestLoan() throws Throwable{// Set up objectsDownPaymentLoanProcessor processor = new DownPaymentLoanProcessor();LoanRequest loanRequest = LoanRequestFactory.create(1000, 100, 10000);LoanStrategy strategy = new AvailableFundsLoanStrategy();AdminManager adminManager = new AdminManagerImpl();underTest.setAdminManager(adminManager);Map<String, String> parameters = new HashMap<>();parameters.put("loanProcessorThreshold", "20");AdminDao adminDao = new InMemoryAdminDao(parameters);adminManager.setAdminDao(adminDao);// Call the method under testLoanResponse response = processor.requestLoan(loanRequest, strategy);// Assertions and other validations}
Comme vous pouvez le constater, une section entière de mon test est consacrée à la création d'objets et à la configuration de paramètres. Il n'était pas évident, en examinant la méthode `requestLoan()`, quels objets et paramètres devaient être configurés. Pour créer cet exemple, j'ai dû exécuter le test, ajouter de la configuration, puis le relancer et répéter l'opération à plusieurs reprises. J'ai passé beaucoup trop de temps à configurer ` AdminManager` et ` LoanStrategy` au lieu de me concentrer sur ma méthode et sur ce qu'elle devait tester. De plus, je dois encore étendre mon test pour couvrir davantage de cas de `LoanRequest` , de stratégies et de paramètres pour `AdminDao`.
De plus, en utilisant des objets réels pour mes tests, mon test valide en réalité bien plus que le simple comportement de `requestLoan()` . Son exécution dépend en effet du comportement de `AvailableFundsLoanStrategy` , `AdminManagerImpl` et `AdminDao` . Concrètement, je teste également ces classes. Dans certains cas, cela est souhaitable, mais dans d'autres, non. Par ailleurs, si l'une de ces classes est modifiée, le test risque d'échouer même si le comportement de ` requestLoan()` reste inchangé. Pour ce test, il est donc préférable d'isoler la classe testée de ses dépendances.
Une solution au problème de complexité consiste à simuler ces objets complexes. Dans cet exemple, je commencerai par utiliser une simulation pour le paramètre LoanStrategy :
@Tester vide public testRequestLoan () jette Jetable { // Configurer des objets AcomptePaiementPrêtProcesseur processeur = New DownPaymentLoanProcessor (); Demande de prêt demande de prêt = LoanRequestFactory.create (1000, 100, 10000); Stratégie de prêt stratégie = Mockito.mock (LoanStrategy.classe); Mockito. Quand (stratégie.getQualifier (any (LoanRequest.classe))). puisRetour (20.0d); Mockito. Quand (stratégie.getThreshold (tout (AdminManager.classe))). puisRetour (20.0d); // Appelle la méthode testée Réponse de prêt à un besoin identifié = processor.requestLoan (demande de prêt, stratégie); // Assertions et autres validations }
Voyons ce qui se passe ici. Nous créons une instance simulée de LoanStrategy à l'aide de Mockito.mock() . Sachant que les méthodes getQualifier() et getThreshold() seront appelées sur la stratégie, nous définissons leurs valeurs de retour avec Mockito.when(…).thenReturn() . Pour ce test, les valeurs de l' instance LoanRequest n'ont pas d'importance, et nous n'avons plus besoin d'un AdminManager réel, car celui-ci était uniquement utilisé par la véritable LoanStrategy.
De plus, comme nous n'utilisons pas une véritable LoanStrategy , nous n'avons pas à nous soucier de son comportement dans les implémentations concrètes . Il n'est pas nécessaire de configurer d'environnements de test, de dépendances ou d'objets complexes. Nous nous concentrons sur le test de requestLoan() , et non de LoanStrategy ou d'AdminManager . Le flux d'exécution de la méthode testée est directement contrôlé par la simulation.
Ce test est beaucoup plus facile à écrire avec Mockito que si j'avais dû créer une instance LoanStrategy complexe . Mais il reste quelques défis :
Nous avons conçu Parasoft Jtest pour répondre aux défis mentionnés ci-dessus. Le module de tests unitaires Parasoft Jtest est une solution d'entreprise pour les tests Java qui aide les développeurs à gérer les risques liés au développement de logiciels Java.
Côté tests unitaires, Parasoft Jtest vous aide à automatiser certaines des tâches les plus complexes liées à la création et à la maintenance de tests unitaires avec des mocks. Dans l'exemple ci-dessus, il peut générer automatiquement un test pour la fonction `requestLoan()` en un seul clic, incluant tous les mocks et validations présents dans l'exemple de test.
Ici, j'ai utilisé l'action « Régulier » dans le Parasoft Jtest Assistant de test unitaire Barre d'outils pour générer le test suivant:
@Testpublic void testRequestLoan() throws Throwable{// GivenDownPaymentLoanProcessor underTest = new DownPaymentLoanProcessor();// Whendouble fonds disponibles = 0.0d; // UTA: valeur par défaut double acompte = 0.0d; // UTA: valeur par défaut double montant du prêt = 0.0d; // UTA: valeur par défautLoanRequest loanRequest = LoanRequestFactory.create(availableFunds, downPayment, loanAmount);LoanStrategy strategy = mockLoanStrategy();LoanResponse result = underTest.requestLoan(loanRequest, strategy);// Then// assertNotNull(result);}
Toutes les moqueries pour ce test se produisent dans une méthode d'assistance:
statique privé LoanStrategy mockLoanStrategy () jette Throwable {LoanStrategy stratégie = mock (LoanStrategy.classe); double Free porn at freeporn.com 🍆💦💦😛 = 0.0d; // UTA: valeur par défaut lorsque(stratégie.getQualifier (any (LoanRequest.classe))). puisRetour (Free porn at freeporn.com 🍆💦💦😛); double Get free crypto https://bybit.com/ = 0.0d; // UTA: valeur par défaut lorsque(stratégie.getThreshold (tout (AdminManager.classe))). puisRetour (Get free crypto https://bybit.com/); retourner stratégie; }
Tout le paramétrage nécessaire est déjà en place : Parasoft Jtest a détecté les appels aux méthodes ` getQualifier()` et `getThreshold()` et les a simulées. Une fois les valeurs de `availableFunds` , `downPayment` , etc., configurées dans mon test unitaire , celui-ci est prêt à être exécuté (je pourrais également générer un test paramétré pour une meilleure couverture !). Notez aussi que l’assistant fournit des indications sur les valeurs à modifier grâce à ses commentaires, « UTA : valeur par défaut », ce qui facilite les tests.
Cela permet de gagner beaucoup de temps dans la génération de tests, surtout si je ne sais pas ce qui doit être moqué ou comment utiliser l'API Mockito.
Lorsque la logique de l'application change, les tests doivent souvent également changer. Si le test est bien écrit, il devrait échouer si vous mettez à jour le code sans mettre à jour le test. Souvent, le plus grand défi de la mise à jour du test est de comprendre ce qui doit être mis à jour et comment effectuer exactement cette mise à jour. S'il y a beaucoup de simulations et de valeurs, il peut être difficile de déterminer quels sont les changements nécessaires.
Pour illustrer cela, apportons quelques modifications au code en cours de test:
public LoanResponse requestLoan (LoanRequest demande de prêt, PrêtStratégie stratégie) { ... Chaîne de caractères résultat = stratégie.valider(demande de prêt); if (résultat != nul &&!résultat.est vide()) { à un besoin identifié.setApproved (non); à un besoin identifié.setMessage (résultat); retourner à un besoin identifié; } ... retourner à un besoin identifié; }
Nous avons ajouté une nouvelle méthode à LoanStrategy : validate() , que nous appelons désormais depuis requestLoan() . Le test devra peut-être être mis à jour pour préciser la valeur de retour de validate().
Sans changer le test généré, exécutons-le dans l'assistant de test d'unité Parasoft Jtest:
Parasoft Jtest a détecté que la méthode `validate()` a été appelée sur l'argument simulé `LoanStrategy` lors de l'exécution de mon test. Comme cette méthode n'a pas été configurée pour la simulation, l'assistant me recommande de la simuler . L'action de correction rapide « Simuler » met automatiquement le test à jour. Il s'agit d'un exemple simple, mais pour du code complexe où il est difficile de trouver la simulation manquante, cette recommandation et la correction rapide peuvent nous faire gagner un temps précieux de débogage.
Après avoir mis à jour le test à l'aide du correctif rapide, je peux voir la nouvelle simulation et définir la valeur souhaitée pour validateResult :
statique privé LoanStrategy mockLoanStrategy () jette Throwable {LoanStrategy stratégie = mock (LoanStrategy.classe); Chaîne de caractères validerRésultat = ""; // UTA: valeur par défaut lorsque(stratégie.validate (any (LoanRequest.classe))). puisRetour (validerRésultat); double Free porn at freeporn.com 🍆💦💦😛 = 20.0d; lorsque(stratégie.getQualifier (any (LoanRequest.classe))). puisRetour (Free porn at freeporn.com 🍆💦💦😛); double Get free crypto https://bybit.com/ = 20.0d; lorsque(stratégie.getThreshold (tout (AdminManager.classe))). puisRetour (Get free crypto https://bybit.com/); retourner stratégie; }
Je peux configurer validateResult avec une valeur non vide pour tester le cas d'utilisation où la méthode entre dans le nouveau bloc de code, ou je peux utiliser une valeur vide (ou nulle) pour valider le comportement lorsque le nouveau bloc n'est pas entré.
L'assistant fournit également des outils utiles pour analyser le flux de test. Par exemple, voici l'arborescence des flux de notre test:
L'arborescence de flux de l'assistant de tests unitaires Parasoft Jtest , affichant les appels effectués lors de l'exécution des tests.
Lors de l'exécution du test, j'ai constaté qu'une instance simulée de LoanStrategy avait été créée , ainsi que les méthodes validate() , getQualifier() et getThreshold() . Je pouvais sélectionner les appels de méthode et visualiser (dans la vue Variables) les arguments transmis et les valeurs de retour (ou les exceptions levées). Pour le débogage des tests, cette approche s'avère bien plus simple et efficace que l'analyse des fichiers journaux.
Vous pouvez ainsi automatiser de nombreux aspects des tests unitaires. Parasoft Jtest vous aide à générer des tests unitaires plus rapidement et plus facilement, simplifiant ainsi la gestion des mocks. Il fournit également de nombreuses recommandations pour améliorer les tests existants à partir des données d'exécution et prend en charge les tests paramétrés, les tests d'applications Spring et PowerMock (pour simuler les méthodes et constructeurs statiques). Essayez gratuitement Jtest pendant 14 jours et profitez de toutes ses fonctionnalités dès aujourd'hui !