web-dev-qa-db-fra.com

Les méthodes privées/protégées doivent-elles être sous test unitaire?

Dans le développement TDD, la première chose à faire est généralement de créer votre interface, puis d'écrire vos tests unitaires sur cette interface. Au fur et à mesure que vous avancez dans le processus TDD, vous créez une classe qui implémente l'interface, puis votre test unitaire réussit à un moment donné.

Maintenant, ma question concerne les méthodes privées et protégées que je pourrais avoir à écrire dans ma classe pour prendre en charge les méthodes/propriétés exposées par l'interface:

  • Les méthodes privées de la classe doivent-elles avoir leurs propres tests unitaires?

  • Les méthodes protégées de la classe doivent-elles avoir leurs propres tests unitaires?

Mes pensées:

  • Surtout parce que je code sur des interfaces, je ne devrais pas m'inquiéter des méthodes protégées/privées, car ce sont des boîtes noires.

  • Comme je suis en train d’utiliser des interfaces, j’écris des tests unitaires pour valider que le contrat défini est correctement implémenté par les différentes classes implémentant l’interface. méthodes/propriétés définies par l'interface.

  • Si ma couverture de code ne montre pas que les méthodes protégées/privées sont touchées, alors je ne dispose pas des tests unitaires adéquats ou du code non utilisé qui doit être supprimé.

67
Raj Rao

Non, je ne pense pas à tester des méthodes privées ou protégées. Les méthodes privées et protégées d'une classe ne font pas partie de l'interface publique, elles n'exposent donc pas le comportement public. Généralement, ces méthodes sont créées par les refactorisations que vous appliquez une fois que votre test est devenu vert.

Donc, ces méthodes privées sont testées implicitement par les tests qui affirment le comportement de votre interface publique.

Sur une note plus philosophique, rappelez-vous que vous testez le comportement, pas les méthodes. Donc, si vous pensez à l'ensemble des choses que la classe sous test peut faire, du moment que vous pouvez tester et affirmer que la classe se comporte comme prévu, s'il existe des méthodes privées (et protégées) utilisées en interne par la classe pour implémenter ce comportement est hors de propos. Ces méthodes sont des détails d'implémentation du comportement public.

94
user122376

Je ne suis pas d'accord avec la plupart des affiches.

La règle la plus importante est la suivante: REGIMES DE CODE DE TRAVAIL RÈGLES THÉORIQUES concernant public/protected/private.

Votre code doit être minutieusement testé. Si vous pouvez y arriver en écrivant des tests pour les méthodes publiques, qui exercent suffisamment les méthodes protégées/privées, c'est génial.

Si vous ne pouvez pas le faire, refactorisez-le pour que vous puissiez le faire ou pliez les règles protected/private.

Il y a une belle histoire sur un psychologue qui a fait passer un test à des enfants. Il a donné à chaque enfant deux planches en bois avec une corde attachée à chaque extrémité, et leur a demandé de traverser une pièce sans toucher leurs pieds au sol, aussi vite que possible. Tous les enfants utilisaient les planches comme des petits skis, un pied sur chaque planche, les tenant par les cordes et glissant sur le sol. Ensuite, il leur a confié la même tâche, mais en n'utilisant qu'UN tableau. Ils ont pivoté/"marché" sur le sol, un pied à chaque extrémité du tableau - et ils étaient PLUS RAPIDES!

Ce n'est pas parce que Java (ou n'importe quel langage) a une fonctionnalité (privée/protégée/publique) que vous écrivez un meilleur code parce que vous l'utilisez!

Maintenant, il y aura toujours moyen d'optimiser/minimiser ce conflit. Dans la plupart des langues, vous pouvez rendre une méthode protégée (au lieu de publique) et placer la classe de test dans le même package (ou autre) et la méthode sera disponible pour le test. Des annotations peuvent aider, comme décrit par d'autres affiches. Vous pouvez utiliser la réflexion pour obtenir des méthodes privées (beurk).

Le contexte compte aussi. Si vous écrivez une API pour une utilisation par des personnes externes, public/privé est plus important. Si c'est un projet interne - qui s'en soucie vraiment?

Mais au bout du compte, réfléchissez au nombre de bugs causés par le manque de tests. Comparez ensuite le nombre de bugs causés par des méthodes "trop ​​visibles". Cette réponse devrait conduire votre décision.

34
Charles Roth

Tu as écrit:

En développement TDD, la première chose à faire vous faites généralement est de créer votre interface et ensuite commencer à écrire votre tests unitaires contre cette interface. Comme vous progressez dans le processus TDD vous finiriez par créer une classe qui implémente l'interface puis à un point votre test unitaire passerait.

Laissez-moi reformuler ceci en BDD language:

Lorsque vous décrivez pourquoi une classe a de la valeur et comment elle se comporte, vous commencez généralement par est de créer un exemple d'utilisation de la classe, souvent via son interface *. Comme vous ajoutez comportement souhaité vous finissez par créer une classe qui fournit cette valeur, puis à certains pointez votre exemple fonctionne.

* Il peut s'agir d'une Interface réelle ou simplement de l'API accessible de la classe, par exemple: Ruby n'a pas d'interfaces.

C'est pourquoi vous ne testez pas les méthodes privées, car un test est un exemple d'utilisation de la classe et vous ne pouvez pas les utiliser. Si vous le souhaitez, vous pouvez par exemple déléguer les responsabilités des méthodes privées à une classe qui collabore, puis simuler/stub cet assistant.

Avec les méthodes protégées, vous dites qu'une classe qui étend votre classe devrait avoir un comportement particulier et fournir une certaine valeur. Vous pouvez ensuite utiliser des extensions de votre classe pour démontrer ce comportement. Par exemple, si vous écriviez une classe de collection ordonnée, vous souhaiterez peut-être démontrer que deux extensions ayant le même contenu démontraient une égalité.

J'espère que cela t'aides!

34
Lunivore

Lorsque vous écrivez les tests unitaires de votre classe, vous ne devez pas nécessairement vous soucier de savoir si les fonctionnalités de la classe sont implémentées directement dans la méthode de l'interface publique ou dans une série de méthodes privées. Alors oui, vous devriez tester vos méthodes privées, mais vous ne devriez pas avoir besoin de les appeler directement depuis votre code de test pour le faire (tester directement les méthodes privées associe étroitement votre implémentation à vos tests et rend la refactorisation inutilement difficile).

Les méthodes protégées constituent un contrat différent entre votre classe et ses futurs enfants. Vous devriez donc vraiment le tester dans la même mesure que votre interface publique pour vous assurer que le contrat est bien défini et exercé.

12
forsvarir

Non! Seules les interfaces de test. 

L'un des gros avantages de TDD est de s'assurer que l'interface fonctionne quelle que soit la manière dont vous avez choisi d'implémenter les méthodes privées.

11
S.Lott

Pour compléter ce que d’autres ont dit plus haut, je dirais que les méthodes protégées font partie d’une sorte d’interface: c’est simplement celle qui est exposée à l’héritage au lieu de la composition, ce à quoi tout le monde a tendance à penser lorsqu’on considère les interfaces.

Marquer une méthode comme étant protégée plutôt que privée signifie qu'elle devrait être utilisée par du code tiers. Il est donc nécessaire de définir et de tester un type de contrat, comme cela se produit avec les interfaces normales définies par des méthodes publiques, ouvertes à la fois pour l'héritage et la composition. .

8
FGM

Il y a deux raisons pour écrire des tests:

  1. Affirmer le comportement attendu
  2. Prévenir la régression du comportement

La prise en compte (1) Affirmation du comportement attendu:

Lorsque vous affirmez le comportement attendu, vous voulez vous assurer que le code fonctionne comme vous le pensez. Il s’agit en fait d’un moyen automatisé d’effectuer votre vérification manuelle de routine que tout développeur réalisera lors de l’implémentation de tout type de code:

  • Est-ce que ce que je viens d'écrire fonctionne?
  • Cette boucle se termine-t-elle réellement?
  • Est-ce que la boucle tourne dans l'ordre que je pense?
  • Cela fonctionnerait-il pour une entrée nulle?

Ce sont des questions auxquelles nous répondons tous dans nos têtes et, normalement, nous essayons également d'exécuter le code dans nos têtes, pour nous assurer que tout se passe bien. Pour ces cas, il est souvent utile que l'ordinateur y réponde de manière définitive. Nous écrivons donc un test unitaire qui le confirme. Cela nous donne confiance dans notre code, nous aide à détecter rapidement les défauts et peut même aider à réellement implémenter le code.

C'est une bonne idée de le faire chaque fois que vous le jugez nécessaire. Tout code qui est un peu difficile à comprendre, ou non trivial. Même un code trivial pourrait en bénéficier. Tout dépend de votre propre confiance. La fréquence et le chemin à parcourir dépendent de votre propre satisfaction. Arrêtez-vous lorsque vous pouvez répondre en toute confiance Oui à: Êtes-vous sûr que cela fonctionne?

Pour ce type de test, vous ne vous souciez pas de la visibilité, des interfaces, ni de rien de tout cela, vous ne vous souciez que du code qui fonctionne. Donc, oui, vous testeriez des méthodes privées et protégées si vous estimiez qu'elles devaient être testées pour que vous puissiez répondre Oui à la question.

La prise en compte (2) Prévention de la régression du comportement:

Une fois que vous avez un code de travail, vous devez mettre en place un mécanisme pour le protéger des dommages futurs. Si personne ne devait jamais plus jamais toucher votre source et votre configuration, vous n'en auriez pas besoin, mais dans la plupart des cas, vous ou d'autres personnes allez toucher à la source et à la configuration de votre logiciel. Ce violonage interne risque fort de casser votre code de travail.

Des mécanismes existent déjà dans la plupart des langues pour se protéger de ces dommages. Les caractéristiques de visibilité sont un mécanisme. Une méthode privée est isolée et masquée. L'encapsulation est un autre mécanisme dans lequel vous compartimentez des objets, de sorte que le changement d'autres compartiments n'affecte pas les autres.

Le mécanisme général pour cela s'appelle: codage à limite. En créant des limites entre des parties du code, vous protégez tout ce qui se trouve à l'intérieur d'une frontière contre tout ce qui se trouve en dehors de celui-ci. Les limites deviennent le point d'interaction et le contrat par lequel les choses interagissent.

Cela signifie que les modifications apportées à une limite, que ce soit en rompant son interface ou en modifiant son comportement attendu, endommageraient et éventuellement briseraient les autres limites qui la dépendaient. C'est pourquoi il est judicieux de disposer d'un test unitaire qui cible ces limites et affirme qu'elles ne changent pas de comportement sémantique.

Ceci est votre test unitaire typique, celui dont la plupart des gens parlent en mentionnant TDD ou BDD. Le but est de durcir les limites et de les protéger du changement. Vous ne voulez pas tester les méthodes privées pour cela, car une méthode privée n'est pas une limite. Les méthodes protégées sont une limite restreinte et je les protégerais. Ils ne sont pas exposés au monde, mais restent exposés à d'autres compartiments ou "Unités".

Que faire de ça?

Comme nous l'avons vu, il existe une bonne raison de tester à l'unité les méthodes publiques et protégées, car nos interfaces ne changent pas. Et il y a aussi une bonne raison de tester des méthodes privées, afin d'affirmer nos travaux d'implémentation. Alors, devrions-nous tous les tester?

Oui et non.

Premièrement : Testez toutes les méthodes pour lesquelles vous estimez avoir besoin d'une preuve définitive que cela fonctionne dans la plupart des cas, afin de pouvoir être sûr que votre code fonctionne, quelle que soit sa visibilité. Ensuite, désactivez ces tests. Ils ont fait leur travail.

Enfin : Ecrivez des tests pour vos limites. Demandez à chaque unité d’utiliser un test qui sera utilisé par d’autres unités de votre système. Assurez-vous que ce test affirme le contrat sémantique, le nom de la méthode, le nombre d'arguments, etc. Et assurez-vous également que le test affirme le comportement disponible de l'unité. Votre test doit montrer comment utiliser l'appareil et ce que l'appareil peut faire. Conservez ces tests activés pour qu'ils s'exécutent sur tous les codes Push.

REMARQUE: vous avez désactivé le premier ensemble de tests pour permettre le travail de refactoring. Un test actif est un couplage de code. Cela empêche toute modification future du code qu'il teste. Vous ne voulez cela que pour vos interfaces et vos contrats d'interaction.

5
Didier A.

Non, vous ne devriez pas tester les méthodes privées (comment voudriez-vous de toute façon sans utiliser quelque chose d'horrible comme la réflexion). Avec les méthodes protégées, cela est un peu moins évident en C #, vous pouvez rendre les choses protégées en interne et je pense que vous pouvez le faire pour tester des classes dérivées qui implémentent toutes leurs fonctionnalités via des méthodes de modèle de modèle.

Mais, en général, si vous pensez que vos méthodes publiques en font trop, il est temps de restructurer vos classes en classes plus atomiques, puis de tester ces classes.

4
briantyler

Moi aussi, je suis d'accord avec la réponse de @ kwbeam sur le fait de ne pas tester les méthodes privées. Cependant, un point important que je voudrais souligner - les méthodes protégées FONT partie de l'API exportée d'une classe et DOIVENT donc être testées. 

Les méthodes protégées ne sont peut-être pas accessibles au public, mais vous fournissez assurément un moyen aux sous-classes de les utiliser/de les remplacer. Quelque chose en dehors de la classe peut y accéder et vous devez donc vous assurer que les membres protégés se comportent comme prévu. Donc, ne testez pas les méthodes privées, mais testez les méthodes publiques et protégées.

Si vous pensez que votre méthode privée contient une logique critique, essayez de l'extraire dans un objet distinct, de l'isoler et de vous permettre de tester son comportement.

J'espère que ça aide!

2
codematix

Je suis d'accord avec tout le monde: la réponse à votre question est «non».

En effet, votre approche et vos réflexions sont tout à fait correctes, notamment en ce qui concerne la couverture de code.

J'ajouterais également que la question (et la réponse «non») s'applique également aux méthodes publiques que vous pourriez introduire dans les classes.

  • Si vous ajoutez des méthodes (publiques/protégées ou privées) parce qu'elles échouent au test, alors vous avez plus ou moins atteint l'objectif de TDD.
  • Si vous ajoutez des méthodes (publiques/protégées ou privées) parce que vous décidez simplement de violer TDD, votre couverture de code devrait les détecter et vous devriez pouvoir améliorer votre processus.

En outre, pour C++ (et je ne devrais penser que pour C++), j'implémente des interfaces à l'aide de méthodes privées uniquement, pour indiquer que la classe ne doit être utilisée que via l'interface implémentée. Cela m'empêche d'appeler par erreur de nouvelles méthodes ajoutées à mon implémentation à partir de mes tests

1
quamrana

Si vous visez une couverture de code élevée (je vous suggère de le faire), vous devez tester toutes vos méthodes, qu'elles soient privées ou protégées.

Protégé est une sorte de point de discussion différent, mais en résumé, il ne devrait pas y en avoir du tout. Soit il casse l’encapsulation sur le code déployé, soit il vous oblige à hériter de cette classe, il suffit de le tester à l’unité, même parfois vous n’avez pas besoin d’hériter.

Cacher une méthode à un client (rendre privé) ne lui donne pas le privilège de ne pas être audité. Par conséquent, ils peuvent être testés par les méthodes publiques mentionnées précédemment.

1
Teoman shipahi

Une bonne conception implique de scinder l’application en plusieurs unités testables. Après cela, certaines unités sont exposées à l'API publique, mais d'autres pas. De plus, les points d'interaction entre les unités exposées et ces unités "internes" ne font pas non plus partie de l'API public.

Je pense qu'une fois que nous aurons l'unité identifiable, nous pourrions tirer parti des tests unitaires, qu'ils soient exposés via une API publique ou non. 

0
h22