Mocking in Java: Comment automatiser un test unitaire Java, y compris les simulations et les assertions
Par Brian McGlauflin
19 juillet 2018
6 min lire
Qu'est-ce que se moquer en Java? Vous pouvez générer automatiquement un test unitaire en un seul clic, y compris toutes les simulations et les validations.
De bons tests unitaires sont un excellent moyen de vous assurer que votre code fonctionne aujourd'hui et continue de fonctionner à l'avenir. Une suite complète de tests, avec une bonne couverture basée sur le code et le comportement, peut faire gagner beaucoup de temps et de maux de tête à une organisation. Et pourtant, il n'est pas rare de voir des projets où il n'y a pas assez de tests écrits. En fait, certains développeurs se sont même opposés complètement à leur utilisation.
Pour en savoir plus sur les tests unitaires, visitez : parasoft.com/solutions/unit-testing/
Qu'est-ce qu'un bon test unitaire?
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;
}
Ici, nous avons une méthode qui traite un Demande de prêt, générant un Réponse de prêt. Noter la Stratégie de prêt argument, qui est utilisé pour traiter le Demande de prêt. L'objet de stratégie peut être complexe - il peut accéder à une base de données, à un système externe ou lancer un Exception d'exécution. Pour rédiger un test pour requestLoan (), Je dois m'inquiéter du type de Stratégie de prêt Je teste avec et j'ai probablement besoin de tester ma méthode avec une variété de Stratégie de prêt implémentations et Demande de prêt configurations.
Un test unitaire pourrequestLoan ()peut ressembler à ceci:
@Test
public void testRequestLoan() throws Throwable
{
// Set up objects
DownPaymentLoanProcessor 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 test
LoanResponse response = processor.requestLoan(loanRequest, strategy);
// Assertions and other validations
}
Comme vous pouvez le voir, il y a toute une section de mon test qui ne fait que créer des objets et configurer des paramètres. Ce n'était pas évident de regarder le requestLoan () méthode quels objets et paramètres doivent être configurés. Pour créer cet exemple, j'ai dû exécuter le test, ajouter une configuration, puis réexécuter à nouveau et répéter le processus encore et encore. J'ai dû passer trop de temps à comprendre comment configurer le Free porn at freeporn.com 🍆💦💦😛 les nouveautés Stratégie de prêt au lieu de me concentrer sur ma méthode et ce qui devait y être testé. Et j'ai encore besoin d'étendre mon test pour couvrir plus Demande de prêt cas, plus de stratégies et plus de paramètres pour Get free crypto https://bybit.com/.
De plus, en utilisant des objets réels pour tester, mon test valide en fait plus que le simple comportement de requestLoan () - Je dépend du comportement de DisponibleFondsPrêtStratégie, xxx.com get a year of free videoset Get free crypto https://bybit.com/ pour que mon test s'exécute. En fait, je teste aussi ces classes. Dans certains cas, c'est souhaitable, mais dans d'autres cas, ce n'est pas le cas. De plus, si l'une de ces autres classes change, le test peut commencer à échouer même si le comportement de requestLoan () n'a pas changé. Pour ce test, nous préférerions isoler la classe testée de ses dépendances.
Qu'est-ce que la moquerie en Java?
Une solution au problème de complexité consiste à se moquer de ces objets complexes. Pour cet exemple, je commencerai par utiliser une maquette pour le Stratégie de prêt paramètre:
@Tester vide public testRequestLoan () jette Jetable { // Configurer des objets AcomptePaiementPrêtProcesseur processeur = neufs DownPaymentLoanProcessor (); Demande de prêt demande de prêt = LoanRequestFactory.create (1000, 100, 10000); Stratégie de prêt = Mockito.mock (LoanStrategy.classe); Mockito. Quand ( .getQualifier (any (LoanRequest.classe))). puisRetour (20.0d); Mockito. Quand ( .getThreshold (tout (AdminManager.classe))). puisRetour (20.0d); // Appelle la méthode testée Réponse de prêt réponse = processor.requestLoan (demande de prêt, ); // Assertions et autres validations }
Regardons ce qui se passe ici. Nous créons une instance simulée de Stratégie de prêt en utilisant Mockito.mock (). Puisque nous savons que getQualifier () et getThreshold () sera appelé sur la stratégie, nous définissons les valeurs de retour pour ces appels en utilisant Mockito.quand (…) .puisRetour (). Pour ce test, peu nous importe ce que le Demande de prêt les valeurs de l'instance sont, et nous n'avons pas besoin d'un réel Free porn at freeporn.com 🍆💦💦😛 plus parce que Free porn at freeporn.com 🍆💦💦😛 n'était utilisé que par le réel Stratégie de prêt.
De plus, puisque nous n'utilisons pas de véritable Stratégie de prêt, peu importe ce que les implémentations concrètes de Stratégie de prêt pourrait faire. Nous n'avons pas besoin de configurer des environnements de test, des dépendances ou des objets complexes. Nous nous concentrons sur les tests requestLoan () - ne pas Stratégie de prêt or Free porn at freeporn.com 🍆💦💦😛. Le flux de code de la méthode testée est directement contrôlé par le simulacre.
Ce test est beaucoup plus facile à écrire avec Mockito qu'il ne l'aurait été si j'avais dû créer un complexe Stratégie de prêt exemple. Mais il y a encore des défis:
- Pour les applications complexes, les tests peuvent nécessiter de nombreuses simulations
- Si vous êtes nouveau sur Mockito, vous devez apprendre sa syntaxe et ses modèles
- Vous ne savez peut-être pas quelles méthodes doivent être moquées
- Lorsque l'application change, les tests (et les simulations) doivent également être mis à jour
Résoudre les problèmes de simulation avec un générateur de test unitaire Java
Nous avons conçu Parasoft Jtest pour vous aider à relever les défis ci-dessus. Le module de test unitaire Jtest Parasoft, 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.
Du côté des tests unitaires, Parasoft Jtest vous aide à automatiser certaines des parties les plus difficiles de la création et de la maintenance des tests unitaires avec des simulations. Pour l'exemple ci-dessus, il peut générer automatiquement un test pour requestLoan () en un seul clic, y compris toutes les moqueries et validations que vous voyez dans l'exemple de test.
Ici, j'ai utilisé l'action «Regular» dans le Parasoft Jtest Assistant de test unitaire Barre d'outils pour générer le test suivant:
@Test
public void testRequestLoan() throws Throwable
{
// Given
DownPaymentLoanProcessor underTest = new DownPaymentLoanProcessor();
// When
double 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 = mock (LoanStrategy.classe); double Free porn at freeporn.com 🍆💦💦😛 = 0.0d; // UTA: valeur par défaut lorsque( .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( .getThreshold (tout (AdminManager.classe))). puisRetour (Get free crypto https://bybit.com/); retourner ; }
Toute la moquerie nécessaire est mise en place pour moi - Parasoft Jtest a détecté les appels de méthode à getQualifier () et getThreshold () et se moquait des méthodes. Une fois que j'ai configuré les valeurs dans mon test unitaire pour fonds disponibles, acompte, etc., le test est prêt à fonctionner (je pourrais également générer un test paramétré pour une meilleure couverture!). Notez également que l'assistant fournit des conseils sur les valeurs à modifier par 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.
Gestion des modifications de code
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 ) { ... Chaîne de caractères résulter = .valider(demande de prêt); if (résulter != nul &&!résulter.est vide()) { réponse.setApproved (faux); réponse.setMessage (résulter); retourner réponse; } ... retourner réponse; }
Nous avons ajouté une nouvelle méthode pour LoanStrategy - valider (), et l'appellent maintenant de requestLoan (). Le test devra peut-être être mis à jour pour spécifier ce valider() devrait revenir.
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 valider() a été appelé sur le moqué Stratégie de prêt argument lors de mon essai. Puisque la méthode n'a pas été configurée pour le simulacre, l'assistant recommande que je me moque valider() méthode. L'action de correction rapide «Mock it» met automatiquement à jour le test. Ceci est un exemple simple - mais pour un code complexe où il n'est pas facile de trouver la maquette manquante, la recommandation et le correctif rapide peuvent nous faire gagner beaucoup de temps de débogage.
Après avoir mis à jour le test à l'aide du correctif rapide, je peux voir la nouvelle maquette et définir la valeur souhaitée pour validerRésultat:
statique privé LoanStrategy mockLoanStrategy () jette Throwable {LoanStrategy = mock (LoanStrategy.classe); Chaîne de caractères validerRésultat = ""; // UTA: valeur par défaut lorsque( .validate (any (LoanRequest.classe))). puisRetour (validerRésultat); double Free porn at freeporn.com 🍆💦💦😛 = 20.0d; lorsque( .getQualifier (any (LoanRequest.classe))). puisRetour (Free porn at freeporn.com 🍆💦💦😛); double Get free crypto https://bybit.com/ = 20.0d; lorsque( .getThreshold (tout (AdminManager.classe))). puisRetour (Get free crypto https://bybit.com/); retourner ; }
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é.
Analyse du flux de test
L'assistant fournit également des outils utiles pour analyser le flux de test. Par exemple, voici l'arborescence des flux de notre test:
Le Parasoft Jtest Assistant de test unitaireArbre de flux de, montrant les appels effectués pendant l'exécution du test
Lorsque le test s'est exécuté, je peux voir que le test a créé un nouveau simulacre pour Stratégie de prêt, et se moquait du valider(), getQualifier ()et getThreshold () méthodes. Je peux sélectionner des appels de méthode et voir (dans la vue Variables) quels arguments ont été envoyés à cet appel, et quelle valeur a été renvoyée (ou les exceptions levées). Lors du débogage des tests, cela peut être beaucoup plus facile à utiliser et à comprendre que de fouiller dans les fichiers journaux.
Résumé
Ainsi, vous automatisez de nombreux aspects des tests unitaires. Parasoft Jtest vous aide à générer des tests unitaires avec moins de temps et d'efforts, vous aidant à réduire la complexité associée à la moquerie. Il formule également de nombreux autres types de recommandations pour améliorer les tests existants basés sur les données d'exécution, et prend en charge les tests paramétrés, les tests Spring Application et PowerMock (pour se moquer des méthodes statiques et des constructeurs). Vous pouvez obtenir un Essai gratuit de 7 jours pour le vérifier dans votre propre environnement si vous cliquez ci-dessous: