Comment tester les méthodes privées ?

Cette question se pose lorsqu’on se met au Test Driven Developpement (TDD). Deux origines possibles : la méthodologie n’a pas été suivie à la lettre et elle nous puni, ou bien nous essayons de faire du TDD sur du code existant, non testé. Dans les deux cas, vous avez un problème de conception.

 Conception émergente.

Vous suivez strictement les règles du TDD. Vous écrivez un test : il échoue. Vous faites un copié-collé bien sale : le test passe. Vous refactorez : vous créez une méthode privée, et vous l’appelez deux fois. Les méthodes privées ainsi créées sont testées à deux endroits, un pour chaque méthode publique. Si vous suivez strictement TDD, vous écrivez vos tests avant vos méthodes. Vous ne devriez donc jamais avoir à vous poser la question « mais comment tester cette méthode ? »

Vous suivez les règles strictement, et vous tombez tout de même sur la question ? Vous avez probablement commencé par écrire le coeur de votre application, sans penser à son intégration. Et celle ci vous réserve des surprises. Jetez votre code, écrivez un petit test, et faites-le passer ! Sinon, lisez le prochain paragraphe.

 J’ai une méthode privée non testée dans mon code. Et maintenant ?

Ce sont des choses qui arrivent chaque jour. Selon votre conception, vous avez trois options : rendre votre méthode publique (voire même l’exposer en tant que service dans une classe différente), la tester via une méthode publique, ou changer sa visibilité en protected (ou package).

Si votre méthode publique est en réalité un service, qui pourrait être réutilisé, pourquoi ne pas la rendre publique ? C’est toujours une option. Vous pouvez même l’extraire dans une classe à part, afin de mieux suivre le Principe de Responsabilité Unique (Single Responsibility Principle).

Si ce n’est pas un service, pourquoi voulez-vous tester cette méthode ? Parce qu’elle est utilisée par une autre méthode de la même classe ? Bien. Traitez la comme si elle faisait partie du code de cette autre méthode. Ecrivez un test pour la méthode publique : cela testera votre méthode privée également. Et vos outils de couverture de code sauront l’identifier.

Parfois, un bug est situé dans une méthode privée. Et vous ne voulez pas recréer tout le contexte nécessaire à appeler la méthode publique. Dans ce cas, considérez la première option : pouvez-vous extraire votre méthode privée dans une classe à part ? Si ce n’est pas le cas, vous pouvez toujours changer la visibilité. Votre méthode est maintenant accessible pour tout le package. Vous pouvez donc écrire une classe de test qui y accèdera.

Il existe d’autres possibilités. Si vous êtes en java, vous pouvez utiliser la réflexion. Si vous êtes en Ruby, vous pouvez utiliser la commande ‘send’. Ce sont des commandes avancées, qu’il faut employer avec précaution.

 

Se poser la question « comment tester une méthode privée » devrait lever une alarme. Vous pourriez surement utiliser un service au lieu d’une méthode privée. Si non, elle devrait être testée comme s’il s’agissait du corps même de la méthode publique l’utilisant.

 

Mots-clés:

7 commentaires pour “Comment tester les méthodes privées ?”

  1. Merci pour ces conseils et précisions.
    Que pensez-vous tout de même de l’utilisation d’API du type de PrivilegedAccessor qui permettent simplement d’accéder aux méthodes privées que l’on souhaite tester ?

  2. Bonjour,
    Je pense que c’est une solution intéressante mais « de dernier recours » car elle complexifie l’écriture des tests. L’utilisation du scope « package protected » me semble du coup un bon compromis entre encapsulation, risque sur le code existant, et simplicité / lisibilité du code des tests.

  3. Bonjour,

    Souvent, il est complexe de mettre en place des tests unitaires. Les développeurs veulent ils avoir la rigueur de se ralentir pour faire du test ? Du coup, il est bon de laisser les tests le plus simple possible.

    Par ailleurs, tester permet souvent de faire une meilleure conception. Déjà parce que cela donne un prétexte pour refactorer. Ensuite, parce que pour tester, il faudra découpler.

    Du coup, je n’ai pas rencontré de cas où ces frameworks me seraient utiles.

    Si vous pensez en mettre sur votre projet, faites moi signe, je suis intéressé !

  4. Si nous avons fait le choix d’utiliser PrivilegedAccessor pour tester les méthodes privées, c’est que nous voulions séparer au mieux les tests du code.
    Or, dans le cas où l’on change la visibilité en protected, cela nous oblige alors à mettre les fichiers de test dans le même package alors que dans ce cas, il est possible de créer un package de test pour chaque package du code métier.

    Il est vrai, comme vous l’avez spécifié dans votre article, que ces méthodes privées devraient être vues directement comme intégrées au code, mais il me parait plus simple de tester une fois pour toute la méthode privée, plutôt qu’à chaque fois qu’elle est appelée au sein de la méthode publique (ces méthodes ont été créées justement pour factoriser du code).

    Ainsi, dans nos fichiers de tests, nous avons simplement a utiliser PrivilegedAccessor comme ceci :
    ======================================
    PrivilegedAccessor.invokeMethod(
    ObjetTest,
    « SignatureMethodePrivee(arguments) »,
    ArgumentsQueLonSouhaitePasserEnEntree);
    ======================================
    Cette methode (invokeMethod) renvoit un objet qu’il suffira juste de caster pour récupérer la valeur de retour de la fonction.

    Nous n’avons pour le moment pas rencontré de difficulté quand à l’utilisation de ce framework. Cela compléxifie légèrement le code de test seulement car PrivilegedAccessor encapsule les exceptions levées par la méthode privée, mais il est possible de récupérer les exceptions d’origine.

    Cependant, il est possible que nous n’ayons pas encore rencontré tous les cas de figure et que cela va nous créer des problèmes par la suite, auquel cas je vous en ferai part.

    Enfin, la combinaison de framework tel que EasyMock avec PrivilegedAccessor se fait aisément.

  5. Merci pour la précision.

    De mon côté, il est impossible d’envoyer du code de test en production. Sous éclipse, il est possible de créer plusieurs « source folder ». Il devient donc possible de créer le même package dans deux dossiers de sources différents.
    Maven propose d’ailleurs cette hiérarchie par défaut : src/main/java pour les classes qui seront déployées, src/test/java pour les classes qui ne seront exécutées que pendant la phase de test.

    Utilisez-vous Maven + Eclipse ou êtes vous dans un autre environnement ? Comment faites vous pour gérer les dépendances ? Et le déploiement ? Avez vous une usine d’intégration continue ?

    Si vous êtes sur Paris, seriez vous disponible un midi pour en discuter ? Sinon, par Skype ?
    jscher@octo.com

  6. Je travaille sous éclipse également, mais je n’utilise pas Maven.
    Cependant, notre objectif final est d’automatiser les tests lors de chaque commit que l’on effectuera pas la suite. Est-ce qu’il est possible de réaliser votre solution (deux dossiers de sources différents) en dehors d’éclipse ? Cela nécessite-t-il un build particulier ?
    En ce qui concerne les dépendances, j’utilise le framework EasyMock qui permet à la fois de couper les dépendances, mais aussi d’intercepter les appels de méthodes pour vérifier que ce sont les bons arguments qui sont passés.
    Comment faites-vous pour gérer les dépendances de votre côté ?

    Enfin, mon projet utilise des EJB 3.0 et le code est déployé sur un serveur WebLogic. Nous avons donc longuement recherché une solution pour effectuer les tests unitaires en coupant les dépendances entre les EJB et la seule solution qui permettait d’effectuer de vrais tests unitaires était de les exécuter en local (nous avons réfléchi à la manière de les exécuter sur le serveur mais cela revenait à faire de la semi-intégration).

    Je suis désolé, mais je ne suis pas sur Paris, et mon ordinateur ne possède ni webcam, ni micro.
    Par contre, nous pouvons continuer de discuter par mail si vous le souhaitez.

  7. Bonjour,

    Je suis d’accord avec l’article : tester les méthodes privées ne sert à rien !
    Cela couple beaucoup trop les tests à l’implémentation, en cassant le principe d’encapsulation. Du coup, le refactoring du code est extrêmement lent, car il faut refactorer beaucoup de tests systématiquement. En plus de ralentir, cela casse le principe du TDD qui consiste à s’appuyer sur des tests verts pour progresser dans le refactoring : les tests étant constamment modifiés, le risque de s’y perdre et que ces tests soient inefficaces est très grand.

    Donc même conseil : pas de tests de méthodes privées, et on sort la méthode dans une nouvelle classe si besoin (SRP), quitte à aboutir à une palanquée de classes qu’avec des méthodes publiques

Laissez un commentaire