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

Mocking in Java: Comment automatiser un test unitaire Java, y compris les simulations et les assertions

Portrait de Brian McGlauflin,
19 juillet 2018
6 min lire

En réduisant la complexité liée à la moquerie, Parasoft Jtest vous permet d'écrire rapidement et facilement des tests unitaires. Découvrez comment automatiser un test unitaire Java, y compris les simulations et les assertions.

Bon 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 dans lesquels trop peu de tests sont rédigés. En fait, certains développeurs se sont même totalement opposés à leur utilisation.

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:

public LoanResponse requestLoan (LoanRequest demande de prêt, PrêtStratégie stratégie)
{     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(loanRequeststrategy);    // 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 🍆💦💦😛 et par 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 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 réponse = processor.requestLoan (demande de prêtstratégie);

    // 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 défis moqueurs avec un générateur de tests unitaires 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éfaut     LoanRequest loanRequest = LoanRequestFactory.create(availableFundsdownPaymentloanAmount);     LoanStrategy strategy = mockLoanStrategy();     LoanResponse result = underTest.requestLoan(loanRequeststrategy);    // 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; }

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 stratégie) { ... Chaîne de caractères résultat = stratégie.valider(demande de prêt);
    if (résultat != nul &&!résultat.est vide()) {
        réponse.setApproved (non);
        réponse.setMessage (résultat);
        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 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é.

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é

Vous pouvez ainsi automatiser 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 ainsi à réduire la complexité associée aux moqueries. 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 et des constructeurs statiques). Commencez votre essai Jtest complet de 14 jours dès aujourd’hui.

Améliorer les tests unitaires pour Java avec l'automatisation