Note du modérateur: Il y a déjà 39 réponses publiées ici (certaines ont été supprimées). Avant de publier votre réponse, demandez-vous si vous pouvez ou non ajouter quelque chose de significatif à la discussion. Vous répétez probablement ce que quelqu'un d'autre a déjà dit.
Je me trouve parfois obligé de rendre une méthode privée dans une classe publique juste pour écrire des tests unitaires pour elle.
Habituellement, cela serait dû au fait que la méthode contient une logique partagée entre d'autres méthodes de la classe et qu'il est plus ordonné de tester la logique seule, ou une autre raison pourrait être possible car je veux tester la logique utilisée dans les threads synchrones sans avoir à se soucier des problèmes de threading .
Est-ce que d'autres personnes se retrouvent à faire ça, parce que je n'aime pas vraiment le faire ?? Personnellement, je pense que les bonus l'emportent sur les problèmes de rendre publique une méthode qui ne fournit pas vraiment de service en dehors de la classe ...
METTRE À JOUR
Merci pour les réponses à tous, semble avoir éveillé l'intérêt des gens. Je pense que le consensus général est que les tests devraient se produire via l'API publique car c'est la seule façon dont une classe sera jamais utilisée, et je suis d'accord avec cela. Les quelques cas que j'ai mentionnés ci-dessus où je ferais cela ci-dessus étaient des cas rares et je pensais que les avantages de le faire en valaient la peine.
Je peux cependant voir tout le monde que cela ne devrait jamais vraiment arriver. Et en y réfléchissant un peu plus, je pense que changer votre code pour prendre en charge les tests est une mauvaise idée - après tout, je suppose que les tests sont en quelque sorte un outil de support et changer un système pour 'supporter un outil de support' si vous voulez, est flagrant mauvaise pratique.
la source
@VisibileForTesting
annotation pour créer de telles méthodes - je vous recommande de le faire pour que la raison pour laquelle la méthode n'est pasprivate
correctement documentée.Réponses:
En règle générale, je suis généralement favorable à la refactorisation du code de "production" pour le rendre plus facile à tester. Cependant, je ne pense pas que ce serait un bon appel ici. Un bon test unitaire (généralement) ne devrait pas se soucier des détails d'implémentation de la classe, seulement de son comportement visible. Au lieu d'exposer les piles internes au test, vous pouvez tester que la classe renvoie les pages dans l'ordre dans lequel vous vous y attendez après avoir appelé
first()
oulast()
.Par exemple, considérez ce pseudo-code:
la source
Personnellement, je préfère faire des tests unitaires en utilisant l'API publique et je ne rendrais certainement jamais la méthode privée publique juste pour la rendre facile à tester.
Si vous voulez vraiment tester la méthode privée de manière isolée, en Java, vous pouvez utiliser Easymock / Powermock pour vous permettre de le faire.
Vous devez être pragmatique à ce sujet et vous devez également être conscient des raisons pour lesquelles les choses sont difficiles à tester.
« Écoutez les tests » - si c'est difficile à tester, cela vous dit-il quelque chose sur votre conception? Pourriez-vous refactoriser où un test pour cette méthode serait trivial et facilement couvert par des tests via l'API publique?
Voici ce que Michael Feathers a à dire dans " Travailler efficacement avec le code hérité "
la source
Comme d'autres l'ont dit, il est quelque peu suspect de tester en bloc des méthodes privées; testez l'interface publique, pas les détails de l'implémentation privée.
Cela dit, la technique que j'utilise lorsque je veux tester un élément qui est privé en C # consiste à rétrograder la protection d'accessibilité de privée à interne, puis à marquer l'assembly de test unitaire en tant qu'assembly ami à l'aide d' InternalsVisibleTo . L'ensemble de tests unitaires sera alors autorisé à traiter les composants internes comme publics, mais vous n'avez pas à vous soucier d'ajouter accidentellement à votre surface publique.
la source
De nombreuses réponses suggèrent de tester uniquement l'interface publique, mais à mon humble avis, cela n'est pas réaliste - si une méthode fait quelque chose qui prend 5 étapes, vous voudrez tester ces cinq étapes séparément, pas toutes ensemble. Cela nécessite de tester les cinq méthodes, qui (autrement que pour les tests) pourraient autrement l'être
private
.La manière habituelle de tester les méthodes "privées" est de donner à chaque classe sa propre interface et de créer les méthodes "privées"
public
, mais pas de les inclure dans l'interface. De cette façon, ils peuvent toujours être testés, mais ils ne gonflent pas l'interface.Oui, cela entraînera un ballonnement de fichier et de classe.
Oui, cela rend les spécificateurs
public
etprivate
redondants.Oui, c'est une douleur dans le cul.
C'est, malheureusement, l'un des nombreux sacrifices que nous faisons pour rendre le code testable . Peut-être qu'un futur langage (ou même une future version de C # / Java) aura des fonctionnalités pour rendre la testabilité des classes et des modules plus pratique; mais en attendant, nous devons sauter à travers ces cerceaux.
Certains diront que chacune de ces étapes devrait être sa propre classe , mais je ne suis pas d'accord - si elles partagent toutes l'état, il n'y a aucune raison de créer cinq classes distinctes où cinq méthodes feraient l'affaire. Pire encore, cela entraîne un ballonnement de fichiers et de classes. De plus , il infecte l'API publique de votre module - toutes ces classes doivent nécessairement l'être
public
si vous voulez les tester à partir d'un autre module (que ce soit, ou inclure le code de test dans le même module, ce qui signifie expédier le code de test avec votre produit) .la source
Un test unitaire devrait tester le marché public, le seul moyen d'utiliser une classe dans d'autres parties du code. Une méthode privée est les détails de l'implémentation, vous ne devriez pas la tester, dans la mesure où l'API publique fonctionne correctement, l'implémentation n'a pas d'importance et pourrait être modifiée sans changements dans les cas de test.
la source
OMI, vous devriez écrire vos tests sans faire d'hypothèses profondes sur la façon dont votre classe a été implémentée à l'intérieur. Vous voudrez probablement le refactoriser plus tard en utilisant un autre modèle interne mais en offrant toujours les mêmes garanties que la mise en œuvre précédente.
Gardant cela à l'esprit, je vous suggère de vous concentrer sur le test de la validité de votre contrat, quelle que soit l'implémentation interne de votre classe. Test basé sur les propriétés de vos API publiques.
la source
Que diriez-vous de le rendre privé? Ensuite, votre code de test peut le voir (ainsi que d'autres classes de votre package), mais il est toujours caché à vos utilisateurs.
Mais vraiment, vous ne devriez pas tester des méthodes privées. Ce sont des détails de mise en œuvre et ne font pas partie du contrat. Tout ce qu'ils font devrait être couvert en appelant les méthodes publiques (s'ils contiennent du code qui n'est pas exercé par les méthodes publiques, cela devrait aller). Si le code privé est trop complexe, la classe fait probablement trop de choses et manque de refactoring.
Rendre une méthode publique est un grand engagement. Une fois que vous aurez fait cela, les gens pourront l'utiliser et vous ne pourrez plus les changer.
la source
Mise à jour : j'ai ajouté une réponse plus étendue et plus complète à cette question dans de nombreux autres endroits. Ceci est disponible sur mon blog .
Si jamais je dois rendre quelque chose public pour le tester, cela indique généralement que le système testé ne suit pas le principe de responsabilité unique . Il y a donc une classe manquante qui devrait être introduite. Après avoir extrait le code dans une nouvelle classe, rendez-le public. Vous pouvez maintenant tester facilement et vous suivez SRP. Votre autre classe n'a qu'à invoquer cette nouvelle classe via la composition.
Rendre les méthodes publiques / utiliser des astuces langauge telles que le marquage du code comme visible pour tester les assemblys devrait toujours être un dernier recours.
Par exemple:
Refactorisez ceci en introduisant un objet validateur.
Il ne nous reste plus qu'à tester que le validateur est correctement appelé. Le processus réel de validation (la logique précédemment privée) peut être testé dans l'isolement pur. Il ne sera pas nécessaire de mettre en place un test complexe pour garantir le succès de cette validation.
la source
De bonnes réponses. Une chose que je n'ai pas vue mentionnée est qu'avec le développement piloté par les tests (TDD), des méthodes privées sont créées pendant la phase de refactoring (regardez Extract Method pour un exemple de modèle de refactoring), et devraient donc déjà avoir la couverture de test nécessaire . Si cela est fait correctement (et bien sûr, vous obtiendrez un mélange d'opinions en matière d'exactitude), vous ne devriez pas avoir à vous soucier de devoir rendre publique une méthode privée juste pour pouvoir la tester.
la source
Pourquoi ne pas diviser l'algorithme de gestion de pile en une classe utilitaire? La classe d'utilité peut gérer les piles et fournir des accesseurs publics. Ses tests unitaires peuvent être axés sur les détails de mise en œuvre. Des tests approfondis pour les classes algorithmiquement délicates sont très utiles pour éliminer les cas marginaux et assurer la couverture.
Ensuite, votre classe actuelle peut déléguer proprement à la classe utilitaire sans exposer aucun détail d'implémentation. Ses tests porteront sur l'exigence de pagination comme d'autres l'ont recommandé.
la source
En java, il est également possible de le rendre privé (c'est-à-dire en laissant le modificateur de visibilité). Si vos tests unitaires sont dans le même package que la classe testée, ils devraient alors pouvoir voir ces méthodes, et sont un peu plus sûrs que de rendre la méthode complètement publique.
la source
Les méthodes privées sont généralement utilisées comme méthodes "auxiliaires". Par conséquent, ils ne renvoient que des valeurs de base et ne fonctionnent jamais sur des instances spécifiques d'objets.
Vous avez quelques options si vous souhaitez les tester.
Vous pouvez également créer une nouvelle classe avec la méthode d'assistance en tant que méthode publique si elle est un assez bon candidat pour une nouvelle classe.
Il y a un très bon article ici à ce sujet.
la source
Si vous utilisez C #, vous pouvez rendre la méthode interne. De cette façon, vous ne polluez pas l'API publique.
Ajoutez ensuite un attribut à la DLL
[assembly: InternalsVisibleTo ("MyTestAssembly")]
Maintenant, toutes les méthodes sont visibles dans votre projet MyTestAssembly. Peut-être pas parfait, mais mieux que de rendre publique une méthode privée juste pour la tester.
la source
Utilisez la réflexion pour accéder aux variables privées si vous en avez besoin.
Mais vraiment, vous ne vous souciez pas de l'état interne de la classe, vous voulez juste tester que les méthodes publiques retournent ce que vous attendez dans les situations que vous pouvez anticiper.
la source
Je dirais que c'est une mauvaise idée car je ne sais pas si vous obtenez des avantages et potentiellement des problèmes en fin de compte. Si vous modifiez le contrat d'un appel, juste pour tester une méthode privée, vous ne testez pas la classe dans la façon dont elle serait utilisée, mais créez un scénario artificiel que vous n'avez jamais voulu voir se produire.
De plus, en déclarant la méthode publique, que dire en six mois (après avoir oublié que la seule raison de rendre une méthode publique est de tester) que vous (ou si vous avez remis le projet) quelqu'un de complètement différent a gagné ne l'utilisez pas, ce qui entraînerait des conséquences potentiellement imprévues et / ou un cauchemar de maintenance.
la source
en termes de tests unitaires, vous ne devez certainement pas ajouter plus de méthodes; Je crois que vous feriez mieux de faire un cas de test à peu près votre
first()
méthode, qui serait appelée avant chaque test; alors vous pouvez appeler plusieurs fois -next()
,previous()
etlast()
de voir si les résultats correspondent à vos attentes. Je suppose que si vous n'ajoutez pas plus de méthodes à votre classe (juste à des fins de test), vous vous en tiendriez au principe de la "boîte noire" du test;la source
Vérifiez d'abord si la méthode doit être extraite dans une autre classe et rendue publique. Si ce n'est pas le cas, faites-le protéger le package et annotez en Java avec @VisibleForTesting .
la source
Dans votre mise à jour, vous dites qu'il est bon de simplement tester en utilisant l'API publique. Il y a en fait deux écoles ici.
Test de boîte noire
L'école de la boîte noire dit que la classe doit être considérée comme une boîte noire dont personne ne peut voir la mise en œuvre à l'intérieur. La seule façon de tester cela est via l'API publique - tout comme l'utilisateur de la classe l'utilisera.
test de boîte blanche.
L'école de la boîte blanche pense naturellement utiliser les connaissances sur la mise en œuvre de la classe et ensuite tester la classe pour savoir qu'elle fonctionne comme il se doit.
Je ne peux vraiment pas prendre parti dans la discussion. Je pensais juste qu'il serait intéressant de savoir qu'il existe deux façons distinctes de tester une classe (ou une bibliothèque ou autre).
la source
Il y a en fait des situations où vous devriez le faire (par exemple lorsque vous implémentez des algorithmes complexes). Il suffit de le faire en paquet privé et ce sera suffisant. Mais dans la plupart des cas, vous avez probablement des classes trop complexes qui nécessitent la factorisation de la logique dans d'autres classes.
la source
Les méthodes privées que vous souhaitez tester isolément indiquent qu'il existe un autre "concept" enfoui dans votre classe. Extrayez ce "concept" dans sa propre classe et testez-le comme une "unité" distincte.
Jetez un oeil à cette vidéo pour une prise vraiment intéressante sur le sujet.
la source
Vous ne devriez jamais laisser vos tests dicter votre code. Je ne parle pas de TDD ou d'autres DD, je veux dire, exactement ce que vous demandez. Votre application a-t-elle besoin de ces méthodes pour être publique? Si c'est le cas, testez-les. Si ce n'est pas le cas, ne les rendez pas publics uniquement pour les tests. Idem pour les variables et autres. Laissez les besoins de votre application dicter le code et laissez vos tests tester que le besoin est satisfait. (Encore une fois, je ne veux pas dire tester d'abord ou pas, je veux dire changer la structure des classes pour atteindre un objectif de test).
Au lieu de cela, vous devriez "tester plus haut". Testez la méthode qui appelle la méthode privée. Mais vos tests doivent tester les besoins de vos applications et non vos "décisions d'implémentation".
Par exemple (bod pseudo code ici);
Il n'y a aucune raison de tester "ajouter", vous pouvez tester les "livres" à la place.
Ne laissez jamais vos tests prendre des décisions de conception de code pour vous. Testez que vous obtenez le résultat attendu, pas comment vous obtenez ce résultat.
la source
J'ai tendance à convenir que les bonus liés à son test unitaire l'emportent sur les problèmes liés à l'augmentation de la visibilité de certains membres. Une légère amélioration consiste à le rendre protégé et virtuel, puis à le remplacer dans une classe de test pour l'exposer.
Alternativement, si c'est une fonctionnalité que vous souhaitez tester séparément, cela ne suggère-t-il pas un objet manquant dans votre conception? Peut-être pourriez-vous le mettre dans une classe testable séparée ... alors votre classe existante délègue simplement à une instance de cette nouvelle classe.
la source
Je garde généralement les classes de test dans le même projet / assemblage que les classes sous test.
De cette façon, je n'ai besoin que de
internal
visibilité pour rendre les fonctions / classes testables.Cela complique quelque peu votre processus de construction, qui doit filtrer les classes de test. J'y parviens en nommant toutes mes classes de test
TestedClassTest
et en utilisant une expression régulière pour filtrer ces classes.Bien sûr, cela ne s'applique qu'à la partie C # / .NET de votre question
la source
Je vais souvent ajouter une méthode appelée quelque chose comme
validate
,verify
,check
, etc., à une classe afin qu'il puisse être appelé à tester l'état interne d'un objet.Parfois, cette méthode est enveloppée dans un bloc ifdef (j'écris principalement en C ++) afin qu'elle ne soit pas compilée pour publication. Mais il est souvent utile dans la version de fournir des méthodes de validation qui parcourent les arbres d'objets du programme en vérifiant les choses.
la source
La goyave a une annotation @VisibleForTesting pour les méthodes de marquage qui ont une portée élargie (package ou public) qu'elles auraient autrement. J'utilise une annotation @Private pour la même chose.
Bien que l'API publique doive être testée, il est parfois pratique et sensé d'accéder à des choses qui ne seraient normalement pas publiques.
Quand:
il semble que la religion l'emporte sur l'ingénierie.
la source
Je laisse généralement ces méthodes en
protected
place et place le test unitaire dans le même package (mais dans un autre projet ou dossier source), où elles peuvent accéder à toutes les méthodes protégées car le chargeur de classe les placera dans le même espace de noms.la source
Non, car il existe de meilleures façons d'écorcher ce chat.
Certains faisceaux de tests unitaires s'appuient sur des macros dans la définition de classe qui se développent automatiquement pour créer des crochets lorsqu'elles sont construites en mode test. Style très C, mais ça marche.
Un idiome OO plus simple consiste à rendre tout ce que vous voulez tester «protégé» plutôt que «privé». Le faisceau de test hérite de la classe en cours de test et peut ensuite accéder à tous les membres protégés.
Ou vous optez pour l'option "ami". Personnellement, c'est la fonctionnalité de C ++ que j'aime le moins car elle enfreint les règles d'encapsulation, mais elle s'avère nécessaire pour la façon dont C ++ implémente certaines fonctionnalités, alors hé ho.
Quoi qu'il en soit, si vous effectuez des tests unitaires, vous devrez tout aussi probablement injecter des valeurs dans ces membres. Le texte de la boîte blanche est parfaitement valide. Et ce vraiment serait briser votre encapsulation.
la source
Dans .Net, il existe une classe spéciale appelée
PrivateObject
spécialement daignée pour vous permettre d'accéder aux méthodes privées d'une classe.En savoir plus sur MSDN ou ici sur Stack Overflow
(Je me demande si personne ne l'a mentionné jusqu'à présent.)
Il y a des situations où cela ne suffit pas, auquel cas vous devrez utiliser la réflexion.
J'adhérerais toujours à la recommandation générale de ne pas tester les méthodes privées, mais comme d'habitude, il y a toujours des exceptions.
la source
Comme cela est largement noté par les commentaires des autres, les tests unitaires devraient se concentrer sur l'API publique. Cependant, mis à part le pour / le contre et la justification, vous pouvez appeler des méthodes privées dans un test unitaire en utilisant la réflexion. Vous devez bien sûr vous assurer que votre sécurité JRE le permet. L'appel de méthodes privées est quelque chose que Spring Framework utilise avec ses ReflectionUtils (voir la
makeAccessible(Method)
méthode).Voici un petit exemple de classe avec une méthode d'instance privée.
Et un exemple de classe qui exécute la méthode d'instance privée.
L'exécution de B, imprimera
Doing something private.
Si vous en avez vraiment besoin, la réflexion peut être utilisée dans les tests unitaires pour accéder aux méthodes d'instance privées.la source
Personnellement, j'ai les mêmes problèmes lorsque je teste des méthodes privées et c'est parce que certains des outils de test sont limités. Ce n'est pas bon que votre conception soit guidée par des outils limités s'ils ne répondent pas à votre besoin, changez l'outil et non la conception. Parce que votre demande de C # je ne peux pas proposer de bons outils de test, mais pour Java, il existe deux outils puissants: TestNG et PowerMock, et vous pouvez trouver des outils de test correspondants pour la plate-forme .NET
la source