Pourquoi les tests unitaires de méthodes privées sont-ils considérés comme une mauvaise pratique?

16

Le contexte:

Je travaille actuellement sur un petit projet en Python. Je structure généralement mes classes avec des méthodes publiques qui sont documentées mais traitent principalement des concepts de haut niveau (ce qu'un utilisateur de la classe doit savoir et utiliser), et un tas de méthodes cachées (en commençant par le soulignement) qui sont en charge de la traitement complexe ou de bas niveau.

Je sais que les tests sont essentiels pour donner confiance au code et pour s'assurer que toute modification ultérieure n'a pas rompu le comportement précédent.

Problème:

Afin de construire les méthodes publiques de niveau supérieur sur une base fiable, je teste généralement les méthodes privées. Je trouve plus facile de savoir si une modification de code a introduit des régressions et où. Cela signifie que ces tests internes peuvent se casser sur des révisions mineures et devront être corrigés / remplacés

Mais je sais aussi que les tests unitaires de méthode privée sont au moins un concept contesté ou plus souvent considéré comme une mauvaise pratique. La raison en est: seul le comportement public doit être testé ( réf. )

Question:

Je tiens à suivre les meilleures pratiques et je voudrais comprendre:

  • Pourquoi l'utilisation de tests unitaires sur des méthodes privées / cachées est-elle mauvaise (quel est le risque)?
  • quelles sont les meilleures pratiques lorsque les méthodes publiques peuvent utiliser un traitement de bas niveau et / ou complexe?

Précisions:

  • ce n'est pas une question de savoir comment . Python n'a pas de véritable concept de confidentialité et les méthodes cachées ne sont tout simplement pas répertoriées mais peuvent être utilisées lorsque vous connaissez leur nom
  • Je n'ai jamais appris de règles et de modèles de programmation: mes derniers cours sont des années 80 ... J'ai principalement appris des langues par essais et échecs et des références sur Internet (Stack Exchange étant mon préféré depuis des années)
Serge Ballesta
la source
2
Copie possible de Test de méthodes privées protégées
Greg Burghardt
2
OP, où avez-vous entendu ou lu que les tests de méthodes privées étaient considérés comme "mauvais"? Il existe différentes façons de tester unité. Voir Test unitaire, Test boîte noire et Test boîte blanche .
John Wu
@JohnWu: Je connais la différence entre les tests en boîte blanche et en boîte noire. Mais même dans les tests en boîte blanche, il semble que la nécessité de tester des méthodes privées soit un indice d'un problème de conception. Ma question est une tentative de comprendre quels sont les meilleurs chemins quand je tombe là-bas ...
Serge Ballesta
2
Encore une fois, où avez-vous entendu ou lu que même dans les tests en boîte blanche, la nécessité de tester des méthodes privées est un indice d'un problème de conception? Je voudrais comprendre le raisonnement derrière cette croyance avant de tenter une réponse.
John Wu
@SergeBallesta en d'autres termes, mettez quelques références à ces articles qui vous ont fait croire que tester des méthodes privées est une mauvaise pratique. Expliquez-nous alors pourquoi les avez-vous crus.
Laiv

Réponses:

17

Quelques raisons:

  1. Généralement, lorsque vous êtes tenté de tester la méthode privée d'une classe, c'est une odeur de conception (classe iceberg, pas assez de composants publics réutilisables, etc.). Il y a presque toujours un problème "plus important" en jeu.

  2. Vous pouvez les tester via l'interface publique (c'est ainsi que vous souhaitez les tester, car c'est ainsi que le client les appellera / les utilisera). Vous pouvez obtenir un faux sentiment de sécurité en voyant le feu vert sur tous les tests réussis pour vos méthodes privées. Il est beaucoup mieux / plus sûr de tester des cas de pointe sur vos fonctions privées via votre interface publique.

  3. Vous risquez une duplication sévère des tests (tests qui semblent très similaires) en testant des méthodes privées. Cela a des conséquences majeures lorsque les exigences changent, car il y aura beaucoup plus de tests que nécessaire. Il peut également vous mettre dans une position où il est difficile de refactoriser à cause de votre suite de tests ... ce qui est l'ultime ironie, car la suite de tests est là pour vous aider à reconcevoir et à refaçonner en toute sécurité!

Un conseil si vous êtes toujours tenté de tester les parties privées (ne l'utilisez pas si cela vous dérange et YMMV, mais cela a bien fonctionné pour moi dans le passé): Parfois, écrire des tests unitaires pour des fonctions privées juste pour vous assurer ils fonctionnent exactement comme vous pensez qu'ils peuvent être utiles (surtout si vous êtes nouveau dans une langue). Cependant, une fois que vous êtes sûr qu'ils fonctionnent, supprimez les tests et assurez-vous toujours que les tests accessibles au public sont solides et se bloqueront si quelqu'un apporte une modification flagrante à ladite fonction privée.

Quand tester des méthodes privées: Puisque cette réponse est devenue (quelque peu) populaire, je me sens obligé de mentionner qu'une "meilleure pratique" est toujours juste cela: une "meilleure pratique". Cela ne signifie pas que vous devez le faire de manière dogmatique ou aveugle. Si vous pensez que vous devriez tester vos méthodes privées et avoir une raison légitime (comme vous écrivez des tests de caractérisation pour une application héritée), alors testez vos méthodes privées . Des circonstances spécifiques l'emportent toujours sur toute règle générale ou meilleure pratique. Soyez simplement conscient de certaines choses qui peuvent mal tourner (voir ci-dessus).

J'ai une réponse qui passe en revue cela en détail sur SO que je ne répéterai pas ici: /programming/105007/should-i-test-private-methods-or-only-public-ones/ 47401015 # 47401015

Matt Messersmith
la source
4
Raison 1: nébuleux. Raison 2: que se passe-t-il si votre méthode d'assistance privée ne doit pas faire partie de l'API publique? Raison 3: Pas si vous concevez correctement votre classe. Votre dernier conseil: pourquoi supprimer un test parfaitement bon qui prouve qu'une méthode que j'ai écrite fonctionne?
Robert Harvey
4
@RobertHarvey Raison 2: être indirectement accessible via l'API publique! = Faire partie de l'API publique. Si votre fonction privée n'est pas testable via l'API publique, alors c'est peut-être un code mort et devrait être supprimé? Ou votre classe est en effet un iceberg (raison 1) et devrait être refactorisée.
Frax
5
@RobertHarvey si vous ne pouvez pas tester une fonction privée via une API publique, supprimez-la car elle ne sert à rien.
David Arno
1
@RobertHarvey 1: Les odeurs de design sont toujours quelque peu subjectives / nébuleuses, donc bien sûr. Mais j'ai énuméré quelques exemples concrets d'anti-modèles, et il y a plus de détails dans ma réponse SO. 2: Les méthodes privées ne peuvent pas faire partie de l'API publique (par définition: elles sont privées) ... donc je ne pense pas que votre question ait beaucoup de sens. J'essaie d'en arriver au point que si vous avez quelque chose comme bin_search(arr, item)(public) et bin_search(arr, item, low, hi)(privé, il y a plusieurs façons de faire une recherche dans le bac), alors tout ce dont vous avez besoin pour tester est le public face à un ( bin_search(arr, item))
Matt Messersmith
1
@RobertHarvey 3: Tout d'abord, j'ai dit risque , pas garantie . Deuxièmement, affirmer "cela fonctionne si vous le faites correctement" est auto-réalisateur. Par exemple, "Vous pouvez écrire un système d'exploitation dans une seule fonction si vous le faites correctement ". Ce n'est pas faux: mais ce n'est pas non plus utile. À propos de l'astuce: vous le supprimeriez parce que si votre implémentation change (c'est-à-dire que vous souhaitez échanger un implément privé), votre suite de tests se mettra en travers de votre chemin (vous aurez un test d'échec là où vous ne devriez pas).
Matt Messersmith
13

Étant donné que l'un des principaux objectifs des tests unitaires est que vous pouvez refactoriser les éléments internes de votre programme et ensuite être en mesure de vérifier que vous n'avez pas cassé sa fonctionnalité, il est contre-productif si vos tests unitaires fonctionnent à un niveau de granularité si fin que toute modification du code du programme vous oblige à réécrire vos tests.

Pete
la source
Je ne sais pas pourquoi votre réponse a été rejetée. C'est court, au point et obtient la réponse 100% correcte.
David Arno
3
@DavidArno: Peut-être parce que tester des méthodes privées n'a pas grand-chose à voir avec la granularité des tests. Cela a tout à voir avec le couplage avec les détails de mise en œuvre.
Robert Harvey
10

L'écriture de tests unitaires pour les méthodes privées lie vos tests unitaires aux détails d'implémentation.

Les tests unitaires doivent tester le comportement d'une classe à la surface extérieure de la classe (c'est l'API publique). Les tests unitaires ne devraient pas avoir à connaître les entrailles d'une classe. L'écriture de tests unitaires par rapport aux détails d'implémentation d'une classe vous lie quand vient le temps de refactoriser. Le refactoring va presque certainement casser ces tests, car ils ne font pas partie de votre API stable.

Cela dit, pourquoi voudriez - vous écrire des tests unitaires pour vos méthodes privées?

Il existe une tension naturelle entre les tests unitaires et le développement incrémentiel. Les développeurs de logiciels qui utilisent une boucle REPL (lecture-évaluation-impression) peuvent attester à quel point il peut être productif d'écrire et de tester rapidement de petites fonctionnalités lorsque vous "développez" une classe ou une fonction. La seule bonne façon de le faire dans des environnements pilotés par des tests unitaires est d'écrire des tests unitaires pour des méthodes privées, mais il y a beaucoup de friction à le faire. Les tests unitaires prennent du temps à écrire, vous avez besoin d'une méthode réelle pour effectuer des tests et votre infrastructure de test doit prendre en charge la possibilité de garder la méthode privée afin qu'elle ne pollue pas votre API externe.

Certains écosystèmes comme C # et .NET ont des moyens de créer des environnements de type REPL (des outils tels que Linqpad le font), mais leur utilité est limitée car vous n'avez pas accès à votre projet. La fenêtre immédiate dans Visual Studio n'est pas pratique; il n'a toujours pas Intellisense complet, vous devez y utiliser des noms pleinement qualifiés et il déclenche une construction chaque fois que vous l'utilisez.

Robert Harvey
la source
4
@ user949300 Il est un peu difficile de débattre de cela sans tomber dans le faux sophisme Scotsman , mais il y a beaucoup de tests mal écrits de toutes sortes. Du point de vue des tests unitaires, vous devez tester le contrat public de votre méthode sans connaître les détails de l'implémentation interne. Il n'est pas toujours faux d'affirmer qu'une certaine dépendance a été appelée X fois: il y a des situations où cela a du sens. Il vous suffit de vous assurer qu'il s'agit bien d'une information que vous souhaitez réellement transmettre dans le contrat de cette unité testée.
Vincent Savard
3
@DavidArno: [haussement d'épaules] Je fais ça depuis un moment maintenant. Les tests unitaires pour les méthodes privées ont toujours bien fonctionné pour moi, jusqu'à ce que Microsoft décide de cesser de prendre en charge les objets proxy dans leur cadre de test. Rien n'a jamais explosé en conséquence. Je n'ai jamais percé un trou dans l'univers en écrivant un test pour une méthode privée.
Robert Harvey
2
@DavidArno: Pourquoi cesserais-je d'utiliser une technique parfaitement bonne qui me procure des avantages, simplement parce que quelqu'un sur Internet dit que c'est une mauvaise idée sans fournir de justification?
Robert Harvey
2
Le principal avantage que je retire des tests unitaires est de me donner un «filet de sécurité» qui me permet de bricoler mon code et d'avoir confiance en sachant que mes changements n'introduisent pas de régressions. À cette fin, le test des méthodes d'assistance privées facilite la recherche de telles régressions. Lorsque je refactorise une méthode d'assistance privée et que j'introduis une erreur logique, je casse des tests spécifiques à cette méthode privée. Si mes tests unitaires étaient plus généraux et ne testaient que l'interface de cette unité de code, le problème serait beaucoup plus obscur à trouver.
Alexander - Rétablir Monica
2
@Frax Bien sûr, je pourrais, mais selon cette logique, je devrais renoncer aux tests unitaires au profit des tests d'intégration à l'échelle du système. Après tout, "dans la plupart des cas, vous devriez pouvoir modifier ces tests pour tester le même comportement"
Alexander - Reinstate Monica
6

D'après mon expérience, j'ai découvert que le test unitaire des classes internes, des méthodes signifie généralement que je dois retirer les fonctions testées, les classes. Pour créer un autre niveau d'abstraction.

Cela conduit à une meilleure adhésion au principe de responsabilité unique.

Robert Andrzejuk
la source
Cela devrait être la réponse acceptée.
Jack Aidley
0

Je pense que c'est une bonne question car elle expose un problème commun dans les tests de couverture. Mais une bonne réponse devrait vous dire que la question n'est pas tout à fait correcte car, en théorie, vous ne devriez pas être en mesure de tester les méthodes privées de manière unitaire . C'est pourquoi ils sont privés .

Peut-être qu'une meilleure question serait "Que dois-je faire quand je veux tester des méthodes privées?" , et la réponse est assez évidente: vous devez les exposer de manière à rendre les tests possibles. Maintenant, cela ne signifie pas nécessairement que vous devez simplement rendre la méthode publique et c'est tout. Vous voudrez probablement faire une abstraction plus élevée; passer à une autre bibliothèque ou API afin que vous puissiez effectuer vos tests sur cette bibliothèque, sans exposer cette fonctionnalité dans votre API principale.

N'oubliez pas qu'il y a une raison pour laquelle vos méthodes ont différents niveaux d'accessibilité, et vous devriez toujours penser à la façon dont vos classes seront utilisées à la fin.

Juan Carlos Eduardo Romaina Ac
la source
J'ai essayé de répondre à cela dans ma question en disant que Python n'a pas de véritable concept de confidentialité et que les méthodes cachées ne sont tout simplement pas répertoriées mais peuvent être utilisées lorsque vous connaissez leur nom
Serge Ballesta