web-dev-qa-db-fra.com

Comment éviter d'avoir à tester les méthodes privées de façon unitaire

Je sais que vous n'êtes pas censé tester des méthodes privées, et si cela semble nécessaire, il pourrait y avoir une classe en attente de sortie.

Mais, je ne veux pas avoir de classes de gazillions juste pour pouvoir tester leurs interfaces publiques et je trouve que pour de nombreuses classes si je teste simplement les méthodes publiques, je finis par devoir me moquer de beaucoup de dépendances et les tests unitaires sont énorme et difficile à suivre.

Je préfère de beaucoup se moquer des méthodes privées lors du test des méthodes publiques et se moquer des dépendances externes lors du test des méthodes privées.

Suis-je fou?

15
Fran Sevillano

Vous avez partiellement raison - vous ne devriez pas directement tester des méthodes privées. Les méthodes privées d'une classe doivent être invoquées par une ou plusieurs des méthodes publiques (peut-être indirectement - une méthode privée appelée par une méthode publique peut invoquer d'autres méthodes privées). Par conséquent, lors du test de vos méthodes publiques, vous testerez également vos méthodes privées. Si vous disposez de méthodes privées non testées, vos cas de test sont insuffisants ou les méthodes privées sont inutilisées et peuvent être supprimées.

Si vous adoptez une approche de test en boîte blanche, vous devez considérer les détails d'implémentation de vos méthodes privées lors de la construction de tests unitaires autour de vos méthodes publiques. Si vous adoptez une approche de boîte noire, vous ne devriez pas tester par rapport aux détails d'implémentation dans les méthodes publiques ou privées, mais contre le comportement attendu.

Personnellement, je préfère une approche en boîte blanche aux tests unitaires. Je peux créer des tests pour mettre les méthodes et les classes sous test dans différents états qui provoquent un comportement intéressant dans mes méthodes publiques et privées, puis affirmer que les résultats sont ce que j'attends.

Donc, ne vous moquez pas de vos méthodes privées. Utilisez-les pour comprendre ce que vous devez tester afin de fournir une bonne couverture des fonctionnalités que vous fournissez. Cela est particulièrement vrai au niveau du test unitaire.

24
Thomas Owens

Je pense que c'est une très mauvaise idée.

Le problème avec la création de tests unitaires de membres privés, c'est qu'elle s'intègre mal avec le cycle de vie du produit.

La raison pour laquelle vous avez CHOISI de rendre ces méthodes privées est qu'elles ne sont pas au cœur de ce que vos classes essaient de faire - juste des aides sur la façon dont vous implémentez actuellement cette fonctionnalité. En refactorisant, ces détails privés sont susceptibles de changer et provoqueront alors des frictions avec le refactoring.

En outre, une différence clé entre les membres publics et privés est que vous devez bien réfléchir à votre API publique, la documenter correctement et la vérifier correctement (assertions, etc.). Mais avec une API privée, il serait inutile de réfléchir aussi attentivement (un effort inutile puisque son utilisation est tellement localisée). Mettre des méthodes privées dans des tests unitaires revient à créer des dépendances externes sur ces méthodes. Cela signifie que l'API doit être stable et bien documentée (car quelqu'un doit comprendre pourquoi ces tests unitaires ont échoué si/quand ils le font).

Je vous suggère:

  • REPENSER si ces méthodes doivent être privées
  • Écrivez des tests ponctuels (juste pour vous assurer que c'est correct maintenant, rendez-le public temporairement afin de pouvoir tester, puis supprimez le test)
  • Construit des tests conditionnels dans votre implémentation à l'aide de #ifdef et d'assertions (les tests ne sont effectués que dans les versions de débogage).

A appréciez votre envie de tester cette fonctionnalité et qu'il est difficile de la tester via votre API publique actuelle. Mais j'apprécie personnellement la modularité plus que la couverture des tests.

4
Lewis Pringle

Les tests unitaires testent le comportement public observable, pas le code, où "public" signifie: valeurs de retour et communication avec les dépendances.

Une "unité" est un code qui résout le même problème (ou plus précisément: a la même raison de changer). Cela peut être une méthode unique ou un groupe de classes.

La principale raison pour laquelle vous ne voulez pas tester private methods est: ils sont détails d'implémentation et vous pouvez les changer pendant refactoring (améliorez votre code en appliquant les principes OO sans changer la fonctionnalité). C'est exactement lorsque vous ne voulez pas que vos tests ne changent pas afin qu'ils puissent garantir que le comportement de votre CuT n'a pas changé pendant le refactoring.

Mais, je ne veux pas avoir de classes gazillion juste pour pouvoir tester leurs interfaces publiques et je trouve que pour de nombreuses classes, si je teste simplement les méthodes publiques, je finis par devoir me moquer de beaucoup de dépendances et les tests unitaires sont énorme et difficile à suivre.

J'expérimente généralement le contraire: plus les classes sont petites (moins elles ont de responsabilités), moins elles ont de dépendances, et plus il est facile à la fois d'écrire et de lire.

Idéalement, vous appliquez le modèle même niveau d'abstraction à vos classes. Cela signifie que vos classes fournissent soit une logique métier (de préférence sous forme de "fonctions pures" travaillant uniquement sur leurs paramètres sans maintenir leur propre état) (x) o appelez des méthodes sur d'autres objets, pas les deux en même temps .

De cette façon, il n'est pas nécessaire de tester le comportement de l'entreprise et les objets de "délégation" sont généralement trop simple pour échouer (pas de branchement, pas de changement d'état) de sorte qu'aucun abandon n'est nécessaire et que leurs tests peuvent être laissés à intégration ou tests de module

3
Timothy Truckle

Les tests unitaires ont une certaine valeur lorsque vous êtes le seul programmeur travaillant sur le code, surtout si l'application est très grande ou très complexe. Lorsque les tests unitaires deviennent essentiels, c'est lorsque vous avez un plus grand nombre de programmeurs travaillant sur la même base de code. Le concept de tests unitaires a été introduit pour résoudre certaines des difficultés de travail dans ces grandes équipes.

La raison pour laquelle les tests unitaires aident les équipes plus importantes est liée aux contrats. Si mon code fait des appels à du code écrit par quelqu'un d'autre, je fais des hypothèses sur ce que le code de l'autre personne va faire dans diverses situations. À condition que ces hypothèses soient toujours vraies, mon code fonctionnera toujours, mais comment savoir quelles hypothèses sont valides et comment savoir quand ces hypothèses ont changé?

C'est là qu'interviennent les tests unitaires. L'auteur d'une classe crée des tests unitaires pour documenter le comportement attendu de leur classe. Le test unitaire définit toutes les manières valides d'utiliser la classe et l'exécution du test unitaire valide le fonctionnement de ces cas d'utilisation comme prévu. Un autre programmeur qui souhaite utiliser votre classe peut lire vos tests unitaires pour comprendre le comportement qu'ils peuvent attendre de votre classe, et l'utiliser comme base pour leurs hypothèses sur le fonctionnement de votre classe.

De cette façon, les signatures de méthode publique de la classe et les tests unitaires forment ensemble un contrat entre l'auteur de la classe et les autres programmeurs qui utilisent cette classe dans leur code.

Dans ce scénario, que se passe-t-il si vous incluez des tests de méthodes privées? De toute évidence, cela n'a aucun sens.

Si vous êtes le seul programmeur à travailler sur votre code et que vous souhaitez utiliser le test unitaire comme moyen de déboguer votre code, je n'y vois aucun inconvénient, c'est juste un outil, et vous pouvez l'utiliser de toute façon qui fonctionne pour vous, mais ce n'était pas la raison pour laquelle les tests unitaires ont été introduits, et ne fournit pas les principaux avantages des tests unitaires.

1
bikeman868

Avant de répondre à une telle question, vous devez décider de ce que vous voulez réellement réaliser.

Vous écrivez du code. Vous espérez qu'il remplit son contrat (en d'autres termes, il fait ce qu'il est censé faire. Écrire ce qu'il est censé faire est un grand pas en avant pour certaines personnes).

Pour être raisonnablement convaincu que le code fait ce qu'il est censé faire, soit vous le regardez suffisamment longtemps, soit vous écrivez du code de test qui teste suffisamment de cas pour vous convaincre "si le code passe tous ces tests alors il est correct".

Souvent, vous n'êtes intéressé que par l'interface définie publiquement d'un code. Si j'utilise votre bibliothèque, peu m'importe comment vous l'avez fait fonctionner correctement, mais seulement qu'elle fonctionne fonctionne correctement. Je vérifie que votre bibliothèque est correcte en effectuant des tests unitaires.

Mais vous créez la bibliothèque. Le faire fonctionner correctement peut être difficile à réaliser. Disons que je ne me soucie que de la bibliothèque effectuant correctement l'opération X, j'ai donc un test unitaire pour X. Vous, le développeur responsable de la création de la bibliothèque, implémentez X en combinant les étapes A, B et C, qui sont chacune totalement non triviales. Pour faire fonctionner votre bibliothèque, vous ajoutez des tests pour vérifier que A, B et C fonctionnent correctement. Vous voulez ces tests. Dire "vous ne devriez pas avoir de tests unitaires pour les méthodes privées" est tout à fait inutile. Vous voulez des tests pour ces méthodes privées. Peut-être quelqu'un vous dit que les tests unitaires de méthodes privées sont faux. Mais cela signifie seulement que vous pourriez ne pas les appeler des "tests unitaires" mais des "tests privés" ou tout ce que vous voulez les appeler.

Le langage Swift résout le problème que vous ne voulez pas exposer A, B, C comme méthodes publiques juste parce que vous voulez le tester en donnant aux fonctions un attribut "testable". Le compilateur permet méthodes testables privées à appeler à partir de tests unitaires, mais pas à partir de code non-test.

1
gnasher729

Restez pragmatique. Tester des cas spéciaux dans des méthodes privées en définissant l'état de l'instance et les paramètres d'une méthode publique de sorte que ces cas se produisent là-bas, est souvent beaucoup trop compliqué.

J'ajoute un accessoire supplémentaire internal (avec le drapeau InternalsVisibleTo l'assembly de test), clairement nommé DoSomethingForTesting(parameters), afin de tester ces méthodes "privées".

Bien sûr, l'implémentation peut changer quelquefois, et ces tests, y compris les accesseurs de test, deviennent obsolètes. C'est encore mieux que les cas non testés ou les tests illisibles.

0
Bernhard Hiller

Oui, vous êtes fou .... COMME UN RENARD!

Il existe plusieurs façons de tester des méthodes privées, dont certaines dépendent de la langue.

  • réflexion! les règles sont faites pour être enfreintes!
  • les faire protéger, hériter et remplacer
  • ami/InternalsVisibleTo classes

Dans l'ensemble, cependant, si vous souhaitez tester des méthodes privées, vous souhaiterez probablement les déplacer vers des méthodes publiques sur une dépendance et tester/injecter cela.

0
Ewan