Dois-je tester des méthodes privées ou uniquement publiques? [fermé]

348

J'ai lu cet article sur la façon de tester des méthodes privées. Je ne les teste généralement pas, car j'ai toujours pensé qu'il était plus rapide de tester uniquement les méthodes publiques qui seront appelées de l'extérieur de l'objet. Testez-vous des méthodes privées? Dois-je toujours les tester?

Patrick Desjardins
la source
"Dois-je tester des assistants privés?" Oui. "Dois-je tester les assistants privés directement?" Cela dépend, généralement si vous pouvez les tester facilement via l'interface publique, pourquoi les tester directement? S'il devient complexe de tester tous les aspects des assistants via une interface publique, le composant a-t-il survécu à son existence en tant qu'unité unique?
Mihai Danila

Réponses:

328

Je ne fais pas de tests unitaires sur les méthodes privées. Une méthode privée est un détail d'implémentation qui doit être caché aux utilisateurs de la classe. Le test des méthodes privées interrompt l'encapsulation.

Si je trouve que la méthode privée est énorme ou complexe ou suffisamment importante pour nécessiter ses propres tests, je la mets simplement dans une autre classe et la rend publique là-bas ( objet méthode ). Ensuite, je peux facilement tester la méthode auparavant privée mais maintenant publique qui vit maintenant dans sa propre classe.

jop
la source
88
Je ne suis pas d'accord. Idéalement, vous écrivez un test rapide avant de commencer à coder une fonction. Pensez à l'entrée type et à la sortie. Écrivez le test (qui ne devrait pas vous prendre plus de quelques secondes) et codez jusqu'à ce que le test soit correct. Il n'y a aucune raison d'abandonner ce style de travail pour des méthodes privées.
Frank
254
Dire que les méthodes privées n'ont pas besoin d'être testées, c'est comme dire qu'une voiture va bien tant qu'elle roule bien, et peu importe ce qui se cache sous le capot. Mais ne serait-il pas agréable de savoir que certains câbles à l'intérieur commencent à se détacher - même si l'utilisateur ne remarque rien? Bien sûr, vous pouvez tout rendre public, mais à quoi ça sert? Vous aurez toujours besoin de méthodes privées.
Frank
37
"Une méthode privée est un détail d'implémentation qui doit être caché aux utilisateurs de la classe." mais les tests sont-ils vraiment du même côté de l'interface de la classe que les utilisateurs "réguliers" (runtime)? ;)
mlvljr
34
Le danger de retirer tout ce que vous voulez tester dans une autre classe est que vous pouvez vous retrouver avec la surcharge de sur-ingénierie de votre produit et avoir un million de composants réutilisables qui ne sont jamais réutilisés.
occulus
44
Comparer un morceau de code à une voiture est faux; le code ne « va pas mal » avec le temps, il est éternel . Si votre test de l'interface publique ne va que jusqu'à déterminer qu'il « semble correct », alors votre test du code public est insuffisant. Dans ce cas, tester des méthodes privées séparément ne rendra pas le test complet, peu importe vos efforts. Concentrez-vous sur le test exhaustif de votre code public dans son ensemble, en utilisant la connaissance du fonctionnement interne du code pour créer les bons scénarios.
rustyx
293

Quel est le but des tests?

Jusqu'à présent, la majorité des réponses disent que les méthodes privées sont des détails d'implémentation qui n'ont pas (ou du moins ne devraient pas) d'importance tant que l'interface publique est bien testée et fonctionne. C'est tout à fait correct si votre seul objectif de test est de garantir le fonctionnement de l'interface publique .

Personnellement, ma principale utilisation pour les tests de code est de garantir que les modifications futures du code ne causent pas de problèmes et d'aider mes efforts de débogage s'ils le font. Je trouve que tester les méthodes privées aussi minutieusement que l'interface publique (sinon plus!) Sert cet objectif.

Considérez: Vous avez la méthode publique A qui appelle la méthode privée B. A et B utilisent tous deux la méthode C. C est modifié (peut-être par vous, peut-être par un fournisseur), ce qui fait que A commence à échouer ses tests. Ne serait-il pas utile d'avoir des tests pour B également, même s'ils sont privés, afin de savoir si le problème est lié à l'utilisation de C par A, à l'utilisation de C par B, ou aux deux?

Les tests de méthodes privées ajoutent également de la valeur dans les cas où la couverture des tests de l'interface publique est incomplète. Bien que ce soit une situation que nous souhaitons généralement éviter, les tests d'unité d'efficacité dépendent à la fois des tests de recherche de bogues et des coûts de développement et de maintenance associés à ces tests. Dans certains cas, les avantages d'une couverture de test à 100% peuvent être jugés insuffisants pour justifier les coûts de ces tests, créant des lacunes dans la couverture de test de l'interface publique. Dans de tels cas, un test bien ciblé d'une méthode privée peut être un ajout très efficace à la base de code.

Dave Sherohman
la source
72
Le problème ici est que ces «futurs changements de code» impliquent invariablement une refactorisation du fonctionnement interne d'une classe. Cela se produit si souvent que la rédaction de tests crée un obstacle à la refactorisation.
Programmeur hors
40
De plus, si vous modifiez continuellement vos tests unitaires, vous avez perdu toute cohérence dans vos tests et vous risquez même de créer des bogues dans les tests unitaires eux-mêmes.
17 de 26
6
@ 17 Si les tests et l'implémentation sont modifiés de manière synchrone (comme, il semble que cela devrait l'être), il y aura beaucoup moins de problèmes.
mlvljr
11
@Sauronlord, La raison pour laquelle vous testez des méthodes privées est que si vous testez uniquement les méthodes publiques, lorsque le test échoue, nous ne savons pas directement où se trouve la cause première de l'échec. Cela pourrait être dans testDoSomething()ou testDoSomethingPrivate(). Cela rend le test moins valable. . Voici plus de raisons pour tester privé stackoverflow.com/questions/34571/… :
Pacerier
3
@Pacerier Il y a aussi une différence entre tester votre code et avoir un processus de test automatisé continu. Vous devez évidemment vous assurer que votre méthode privée fonctionne, mais vous ne devriez pas avoir de tests vous couplant à la méthode privée, car cela ne fait pas partie du cas d'utilisation du logiciel.
Didier A.
150

J'ai tendance à suivre les conseils de Dave Thomas et Andy Hunt dans leur livre Pragmatic Unit Testing :

En général, vous ne voulez pas rompre l'encapsulation pour le test (ou comme maman disait, "n'exposez pas vos soldats!"). La plupart du temps, vous devriez pouvoir tester une classe en exerçant ses méthodes publiques. S'il y a des fonctionnalités importantes cachées derrière un accès privé ou protégé, cela pourrait être un signe d'avertissement qu'il y a une autre classe qui a du mal à sortir.

Mais parfois, je ne peux pas m'empêcher de tester des méthodes privées car cela me rassure que je construis un programme complètement robuste.

Rosellyne Worrall
la source
9
Je recommanderais de désactiver les tests unitaires qui ciblent les méthodes privées. Ils sont un couplage de code, et surchargeront les futurs travaux de refactorisation, ou même parfois entraveront l'ajout ou la modification de fonctionnalités. Il est bon de rédiger un test pour eux au fur et à mesure que vous les implémentez, comme moyen automatisé de vous assurer que votre implémentation fonctionne, mais il n'est pas avantageux de conserver les tests comme régression.
Didier A.
61

Je me sens un peu obligé de tester des fonctions privées car je suis de plus en plus l'une de nos dernières recommandations QA dans notre projet:

Pas plus de 10 en complexité cyclomatique par fonction.

Maintenant, l'effet secondaire de l'application de cette politique est que beaucoup de mes très grandes fonctions publiques sont divisées en de nombreuses fonctions privées plus ciblées et mieux nommées .
La fonction publique est toujours là (bien sûr) mais est essentiellement réduite à toutes ces «sous-fonctions» privées

C'est vraiment cool, car la pile d'appels est maintenant beaucoup plus facile à lire (au lieu d'un bogue dans une grande fonction, j'ai un bogue dans une sous-sous-fonction avec le nom des fonctions précédentes dans la pile d'appels pour m'aider à comprendre 'comment j'y suis arrivé')

Cependant, il semble maintenant plus facile de tester directement ces fonctions privées et de laisser le test de la grande fonction publique à une sorte de test «d'intégration» où un scénario doit être traité.

Juste mes 2 cents.

VonC
la source
2
pour réagir à @jop, je ne ressens pas le besoin d'exporter ces fonctions privées (créées à cause de la division d'une grande fonction publique complexe trop cyclomatique) dans une autre classe. J'aime les avoir toujours étroitement associés à la fonction publique, dans la même classe. Mais toujours testé à l'unité.
VonC
2
D'après mon expérience, ces méthodes privées ne sont que des méthodes utilitaires qui sont réutilisées par ces méthodes publiques. Parfois, il est plus pratique de diviser la classe d'origine en deux (ou trois) classes plus cohérentes, rendant ces méthodes privées publiques dans leurs propres classes, et donc testables.
JOP
7
Non, dans mon cas, ces nouvelles fonctions privées font vraiment partie de l'algorithme plus large représenté par la fonction publique. Cette fonction est divisée en parties plus petites, qui ne sont pas utiles, mais les étapes d'un processus plus vaste. D'où la nécessité de les tester unitairement (plutôt que de tester unitairement tout l'algo à la fois)
VonC
Pour ceux qui s'intéressent à la complexité cyclomatique, j'ai ajouté une question sur le sujet: stackoverflow.com/questions/105852/…
VonC
Oups, l'URL de la question a changé en raison d'une faute de frappe dans le titre! stackoverflow.com/questions/105852/…
VonC
51

Oui, je teste des fonctions privées, car bien qu'elles soient testées par vos méthodes publiques, il est agréable dans TDD (Test Driven Design) de tester la plus petite partie de l'application. Mais les fonctions privées ne sont pas accessibles lorsque vous êtes dans votre classe d'unité de test. Voici ce que nous faisons pour tester nos méthodes privées.

Pourquoi avons-nous des méthodes privées?

Les fonctions privées existent principalement dans notre classe car nous voulons créer du code lisible dans nos méthodes publiques. Nous ne voulons pas que l'utilisateur de cette classe appelle ces méthodes directement, mais via nos méthodes publiques. De plus, nous ne voulons pas changer leur comportement lors de l'extension de la classe (en cas de protection), c'est donc un privé.

Lorsque nous codons, nous utilisons la conception pilotée par les tests (TDD). Cela signifie que parfois nous tombons sur une fonctionnalité privée et que nous voulons tester. Les fonctions privées ne sont pas testables dans phpUnit, car nous ne pouvons pas y accéder dans la classe Test (elles sont privées).

Nous pensons que voici 3 solutions:

1. Vous pouvez tester vos privés grâce à vos méthodes publiques

Les avantages

  • Tests unitaires simples (pas de «hacks» nécessaires)

Désavantages

  • Le programmeur doit comprendre la méthode publique, alors qu'il ne veut tester que la méthode privée
  • Vous ne testez pas la plus petite partie testable de l'application

2. Si le privé est si important, alors peut-être que c'est un code pour créer une nouvelle classe séparée pour lui

Les avantages

  • Vous pouvez refactoriser cela à une nouvelle classe, car si c'est si important, d'autres classes peuvent en avoir besoin aussi
  • L'unité testable est maintenant une méthode publique, donc testable

Désavantages

  • Vous ne voulez pas créer une classe si elle n'est pas nécessaire, et uniquement utilisée par la classe d'où provient la méthode
  • Perte de performances potentielle en raison de la surcharge supplémentaire

3. Changez le modificateur d'accès en (final) protégé

Les avantages

  • Vous testez la plus petite partie testable de l'application. Lors de l'utilisation de final protected, la fonction ne sera pas remplaçable (comme un privé)
  • Aucune perte de performance
  • Pas de frais généraux supplémentaires

Désavantages

  • Vous modifiez un accès privé à protégé, ce qui signifie qu'il est accessible par ses enfants
  • Vous avez toujours besoin d'une classe Mock dans votre classe de test pour l'utiliser

Exemple

class Detective {
  public function investigate() {}
  private function sleepWithSuspect($suspect) {}
}
Altered version:
class Detective {
  public function investigate() {}
  final protected function sleepWithSuspect($suspect) {}
}
In Test class:
class Mock_Detective extends Detective {

  public test_sleepWithSuspect($suspect) 
  {
    //this is now accessible, but still not overridable!
    $this->sleepWithSuspect($suspect);
  }
}

Notre unité de test peut donc maintenant appeler test_sleepWithSuspect pour tester notre ancienne fonction privée.

eddy147
la source
eddy147, i j'aime vraiment le concept de tester des méthodes protégées via des simulacres. Merci!!!!
Theodore R. Smith
15
Je veux juste souligner que dans la description originale de TDD, dans les tests unitaires, l' unité est la classe , pas une méthode / fonction. Ainsi, lorsque vous mentionnez "tester la plus petite partie de l'application", il est faux de faire référence à la plus petite partie testable comme méthode. Si vous utilisez cette logique, vous pourriez tout aussi bien parler d'une seule ligne de code au lieu d'un bloc de code entier.
Matt Quigley
@Matt Une unité de travail peut pointer vers une classe, mais aussi une seule méthode.
eddy147
4
@ eddy147 Les tests unitaires viennent avec le développement piloté par les tests, où l'unité a été définie comme une classe. Comme cela se produit avec The Internets, la sémantique s'est élargie pour signifier beaucoup de choses (c'est-à-dire demander à 2 personnes quelle est la différence entre les tests unitaires et les tests d'intégration, et vous obtiendrez 7 réponses). TDD était conçu comme un moyen d'écrire un logiciel avec des principes SOLIDES, y compris la responsabilité unique, où une classe avait une seule responsabilité et ne devrait pas avoir une complexité cyclique élevée.Dans TDD, vous écrivez votre classe et testez ensemble, les deux unités. Les méthodes privées sont encapsulées n'ont pas de test unitaire correspondant.
Matt Quigley
"Lorsque nous codons, nous utilisons la conception pilotée par les tests (TDD). Cela signifie que parfois nous tombons sur une fonctionnalité privée et que nous voulons tester." Je suis fortement en désaccord avec cette affirmation, veuillez consulter ma réponse ci-dessous pour plus de détails. TDD ne signifie pas que vous êtes obligé de tester des méthodes privées. Vous pouvez choisir de tester des méthodes privées: c'est votre choix, mais ce n'est pas TDD qui vous fait faire une telle chose.
Matt Messersmith
41

Je n'aime pas tester les fonctionnalités privées pour plusieurs raisons. Ils sont les suivants (ce sont les principaux points pour les personnes TLDR):

  1. Généralement, lorsque vous êtes tenté de tester la méthode privée d'une classe, c'est une odeur de conception.
  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. Cela 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é!

Je vais expliquer chacun d'eux avec un exemple concret. Il s'avère que 2) et 3) sont quelque peu intimement liés, donc leur exemple est similaire, bien que je les considère comme des raisons distinctes pour lesquelles vous ne devriez pas tester des méthodes privées.

Il est parfois nécessaire de tester des méthodes privées, il est juste important d'être conscient des inconvénients énumérés ci-dessus. Je vais l'examiner plus en détail plus tard.

Je passe également en revue pourquoi TDD n'est pas une excuse valable pour tester des méthodes privées à la toute fin.

Refactorisation de votre sortie d'un mauvais design

L'un des paternités (anti) les plus courantes que je vois est ce que Michael Feathers appelle une classe "Iceberg" (si vous ne savez pas qui est Michael Feathers, allez acheter / lire son livre "Working Effectively with Legacy Code". Il est une personne à savoir si vous êtes un ingénieur / développeur de logiciels professionnel). Il existe d'autres modèles (anti) qui provoquent ce problème, mais c'est de loin le plus courant que j'ai rencontré. Les classes "Iceberg" ont une méthode publique, et les autres sont privées (c'est pourquoi il est tentant de tester les méthodes privées). Cela s'appelle une classe "Iceberg" car il y a généralement une méthode publique solitaire, mais le reste de la fonctionnalité est caché sous l'eau sous forme de méthodes privées.

Évaluateur de règles

Par exemple, vous pourriez vouloir tester GetNextToken()en l'appelant successivement sur une chaîne et en voyant qu'il renvoie le résultat attendu. Une fonction comme celle-ci mérite un test: ce comportement n'est pas anodin, surtout si vos règles de tokenisation sont complexes. Imaginons que ce ne soit pas si complexe, et nous voulons simplement enchaîner des jetons délimités par l'espace. Donc, vous écrivez un test, peut-être qu'il ressemble à quelque chose comme ça (un pseudo-code indépendant du langage, j'espère que l'idée est claire):

TEST_THAT(RuleEvaluator, canParseSpaceDelimtedTokens)
{
    input_string = "1 2 test bar"
    re = RuleEvaluator(input_string);

    ASSERT re.GetNextToken() IS "1";
    ASSERT re.GetNextToken() IS "2";
    ASSERT re.GetNextToken() IS "test";
    ASSERT re.GetNextToken() IS "bar";
    ASSERT re.HasMoreTokens() IS FALSE;
}

Eh bien, ça a l'air plutôt sympa. Nous souhaitons nous assurer de conserver ce comportement lorsque nous apportons des modifications. Mais GetNextToken()c'est une fonction privée ! Nous ne pouvons donc pas le tester comme ceci, car il ne compilera même pas (en supposant que nous utilisons un langage qui applique réellement public / privé, contrairement à certains langages de script comme Python). Mais qu'en est-il du changement de RuleEvaluatorclasse pour suivre le principe de responsabilité unique (principe de responsabilité unique)? Par exemple, nous semblons avoir un analyseur, un tokenizer et un évaluateur coincés dans une seule classe. Ne serait-il pas préférable de simplement séparer ces responsabilités? En plus de cela, si vous créez une Tokenizerclasse, ses méthodes publiques seraientHasMoreTokens() et GetNextTokens(). La RuleEvaluatorclasse pourrait avoir unTokenizerobjet en tant que membre. Maintenant, nous pouvons garder le même test que ci-dessus, sauf que nous testons la Tokenizerclasse au lieu de la RuleEvaluatorclasse.

Voici à quoi cela pourrait ressembler en UML:

Évaluateur de règles refactorisé

Notez que cette nouvelle conception augmente la modularité, vous pouvez donc potentiellement réutiliser ces classes dans d'autres parties de votre système (avant vous, les méthodes privées ne sont pas réutilisables par définition). C'est le principal avantage de décomposer l'évaluateur de règles, ainsi qu'une meilleure compréhensibilité / localité.

Le test serait extrêmement similaire, sauf qu'il se compilerait cette fois puisque la GetNextToken()méthode est maintenant publique sur la Tokenizerclasse:

TEST_THAT(Tokenizer, canParseSpaceDelimtedTokens)
{
    input_string = "1 2 test bar"
    tokenizer = Tokenizer(input_string);

    ASSERT tokenizer.GetNextToken() IS "1";
    ASSERT tokenizer.GetNextToken() IS "2";
    ASSERT tokenizer.GetNextToken() IS "test";
    ASSERT tokenizer.GetNextToken() IS "bar";
    ASSERT tokenizer.HasMoreTokens() IS FALSE;
}

Tester des composants privés via une interface publique et éviter la duplication des tests

Même si vous ne pensez pas pouvoir décomposer votre problème en moins de composants modulaires (ce que vous pouvez faire 95% du temps si vous essayez de le faire), vous pouvez simplement tester les fonctions privées via une interface publique. Plusieurs fois, les membres privés ne valent pas la peine d'être testés car ils seront testés via l'interface publique. Souvent, ce que je vois, ce sont des tests qui se ressemblent beaucoup , mais testez deux fonctions / méthodes différentes. Ce qui finit par arriver, c'est que lorsque les exigences changent (et elles le font toujours), vous avez maintenant 2 tests cassés au lieu de 1. Et si vous avez vraiment testé toutes vos méthodes privées, vous pourriez avoir plus de 10 tests cassés au lieu de 1. En bref , tester des fonctions privées (en utilisantFRIEND_TESTou les rendre publics ou en utilisant la réflexion) qui pourraient autrement être testés via une interface publique peuvent entraîner la duplication des tests et / ou de réflexion, vous finirez généralement par le regretter à long terme.. Vous ne voulez vraiment pas cela, car rien ne fait plus mal que votre suite de tests qui vous ralentit. Il est censé réduire le temps de développement et les coûts de maintenance! Si vous testez des méthodes privées qui sont autrement testées via une interface publique, la suite de tests peut très bien faire le contraire, et augmenter activement les coûts de maintenance et augmenter le temps de développement. Lorsque vous rendez une fonction privée publique, ou si vous utilisez quelque chose commeFRIEND_TEST

Considérez l'implémentation possible suivante de la Tokenizerclasse:

entrez la description de l'image ici

Disons que SplitUpByDelimiter()c'est responsable du retour d'un tableau tel que chaque élément du tableau est un jeton. De plus, disons simplement que GetNextToken()c'est simplement un itérateur sur ce vecteur. Ainsi, votre test public pourrait ressembler à ceci:

TEST_THAT(Tokenizer, canParseSpaceDelimtedTokens)
{
    input_string = "1 2 test bar"
    tokenizer = Tokenizer(input_string);

    ASSERT tokenizer.GetNextToken() IS "1";
    ASSERT tokenizer.GetNextToken() IS "2";
    ASSERT tokenizer.GetNextToken() IS "test";
    ASSERT tokenizer.GetNextToken() IS "bar";
    ASSERT tokenizer.HasMoreTokens() IS false;
}

Imaginons que nous ayons ce que Michael Feather appelle un outil de tâtonnement . Il s'agit d'un outil qui vous permet de toucher les parties intimes des autres. Un exemple est FRIEND_TESTde googletest, ou de réflexion si la langue le supporte.

TEST_THAT(TokenizerTest, canGenerateSpaceDelimtedTokens)
{
    input_string = "1 2 test bar"
    tokenizer = Tokenizer(input_string);
    result_array = tokenizer.SplitUpByDelimiter(" ");

    ASSERT result.size() IS 4;
    ASSERT result[0] IS "1";
    ASSERT result[1] IS "2";
    ASSERT result[2] IS "test";
    ASSERT result[3] IS "bar";
}

Eh bien, disons maintenant que les exigences changent et que la tokenisation devient beaucoup plus complexe. Vous décidez qu'un simple délimiteur de chaîne ne suffira pas et vous avez besoin d'une Delimiterclasse pour gérer le travail. Naturellement, vous vous attendez à ce qu'un test se brise, mais cette douleur augmente lorsque vous testez des fonctions privées.

Quand les tests de méthodes privées peuvent-ils être appropriés?

Il n'y a pas de "solution unique" dans le logiciel. Parfois, c'est correct (et en fait idéal) de "briser les règles". Je recommande fortement de ne pas tester les fonctionnalités privées lorsque vous le pouvez. Il y a deux situations principales quand je pense que ça va:

  1. J'ai beaucoup travaillé avec les systèmes hérités (c'est pourquoi je suis un grand fan de Michael Feathers), et je peux dire en toute sécurité qu'il est parfois tout simplement plus sûr de simplement tester la fonctionnalité privée. Il peut être particulièrement utile pour obtenir des «tests de caractérisation» dans la ligne de base.

  2. Vous êtes pressé et devez faire la chose la plus rapide possible ici et maintenant. À long terme, vous ne voulez pas tester des méthodes privées. Mais je dirai qu'il faut généralement un certain temps pour refactoriser les problèmes de conception. Et parfois, vous devez expédier dans une semaine. Ce n'est pas grave: faites vite et sale et testez les méthodes privées à l'aide d'un outil de tâtonnement si c'est ce que vous pensez être le moyen le plus rapide et le plus fiable pour faire le travail. Mais comprenez que ce que vous avez fait n'était pas optimal à long terme, et pensez à y revenir (ou, s'il a été oublié mais que vous le voyez plus tard, corrigez-le).

Il y a probablement d'autres situations où ça va. Si vous pensez que tout va bien et que vous avez une bonne justification, faites-le. Personne ne vous arrête. Soyez juste conscient des coûts potentiels.

L'excuse TDD

En passant, je n'aime vraiment pas que les gens utilisent TDD comme excuse pour tester des méthodes privées. Je pratique le TDD et je ne pense pas que le TDD vous oblige à le faire. Vous pouvez d'abord écrire votre test (pour votre interface publique), puis écrire du code pour satisfaire cette interface. Parfois, j'écris un test pour une interface publique, et je le satisfait en écrivant également une ou deux méthodes privées plus petites (mais je ne teste pas directement les méthodes privées, mais je sais qu'elles fonctionnent ou mon test public échouerait) ). Si je dois tester des cas limites de cette méthode privée, j'écrirai tout un tas de tests qui les atteindront via mon interface publique.Si vous ne savez pas comment frapper les cas extrêmes, c'est un signe fort que vous devez refactoriser en petits composants chacun avec leurs propres méthodes publiques. C'est un signe que vos fonctions privées en font trop et sortent du cadre de la classe .

De plus, parfois je trouve que j'écris un test qui est trop gros pour mordre pour le moment, et donc je pense "eh je reviendrai à ce test plus tard quand j'aurai plus d'une API pour travailler" (je Je vais le commenter et le garder dans le fond de mon esprit). C'est là que de nombreux développeurs que j'ai rencontrés commenceront alors à écrire des tests pour leurs fonctionnalités privées, en utilisant TDD comme bouc émissaire. Ils disent "oh, eh bien j'ai besoin d'un autre test, mais pour écrire ce test, j'ai besoin de ces méthodes privées. Par conséquent, comme je ne peux pas écrire de code de production sans écrire un test, j'ai besoin d'écrire un test pour une méthode privée. " Mais ce qu'ils doivent vraiment faire, c'est refactoriser en composants plus petits et réutilisables au lieu d'ajouter / tester un tas de méthodes privées à leur classe actuelle.

Remarque:

Il y a quelques temps, j'ai répondu à une question similaire sur le test de méthodes privées à l'aide de GoogleTest . J'ai surtout modifié cette réponse pour être plus indépendant de la langue ici.

PS Voici la conférence pertinente sur les classes d'iceberg et les outils de tâtonnement de Michael Feathers: https://www.youtube.com/watch?v=4cVZvoFGJTU

Matt Messersmith
la source
Le problème que j'ai avec la liste «vous pourriez potentiellement réutiliser ces classes dans d'autres parties de votre système» comme un avantage, c'est que parfois la raison pour laquelle je marque une fonction est privée parce que je ne veux pas qu'elle soit utilisée par d'autres parties de le système. C'est un problème spécifique au langage: idéalement, ce serait privé à un "module", mais si le langage ne le supporte pas (par exemple PHP), ma classe représente le module, pas l'unité: les méthodes privées sont du code réutilisable avec leurs propres contrats, mais ne doivent être réutilisés que dans cette classe.
IMSoP
Je comprends ce que vous dites, mais j'aime la façon dont la communauté Python gère ce problème. Si vous nommez le membre "privé" en question avec un interligne _, cela indique "hé, c'est" privé ". Vous pouvez l'utiliser, mais la divulgation complète, il n'a pas été conçu pour être réutilisé et vous ne devez l'utiliser que si vous vraiment savoir ce que vous faites ". Vous pouvez adopter la même approche dans n'importe quelle langue: rendre ces membres publics, mais marquez-les avec un leader _. Ou peut-être que ces fonctions devraient vraiment être privées et simplement testées via une interface publique (voir la réponse pour plus de détails). C'est au cas par cas, pas de règle générale
Matt Messersmith
26

Je pense qu'il est préférable de simplement tester l'interface publique d'un objet. Du point de vue du monde extérieur, seul le comportement de l'interface publique est important et c'est vers cela que vos tests unitaires doivent être orientés.

Une fois que vous avez écrit des tests unitaires solides pour un objet, vous ne voulez pas avoir à revenir en arrière et à modifier ces tests simplement parce que l'implémentation derrière l'interface a changé. Dans cette situation, vous avez ruiné la cohérence de vos tests unitaires.

17 sur 26
la source
21

Si votre méthode privée n'est pas testée en appelant vos méthodes publiques, que fait-elle? Je parle privé non protégé ou ami.

chrissie1
la source
3
Je vous remercie. Il s'agit d'un commentaire étonnamment sous-estimé et surtout toujours d'actualité, même après près de 8 ans depuis sa rédaction.
Sauronlord
1
Avec le même raisonnement, on pourrait prétendre ne tester le logiciel qu'à partir de l'interface utilisateur (test au niveau du système), car en quelque sorte chaque fonction du logiciel serait exécutée d'une manière ou d'une autre à partir de là.
Dirk Herrmann
18

Si la méthode privée est bien définie (c'est-à-dire qu'elle a une fonction qui peut être testée et n'est pas censée changer au fil du temps), alors oui. Je teste tout ce qui est testable là où cela a du sens.

Par exemple, une bibliothèque de chiffrement peut masquer le fait qu'elle effectue un chiffrement par blocs avec une méthode privée qui ne chiffre que 8 octets à la fois. J'écrirais un test unitaire pour cela - ce n'est pas censé changer, même s'il est caché, et s'il se casse (en raison des améliorations futures des performances, par exemple), alors je veux savoir que c'est la fonction privée qui a cassé, pas seulement que l'une des fonctions publiques a éclaté.

Il accélère le débogage plus tard.

-Adam

Adam Davis
la source
1
Dans ce cas, ne serait-il pas logique de déplacer cette méthode privée vers une autre classe, puis de la rendre publique ou statique publique?
Programmeur hors
+1 Si vous ne testez pas vos fonctions de membre privé et que votre test de l'interface publique échoue, tout ce que vous obtiendrez sera un résultat équivalent à quelque chose qui est cassé sans aucune idée de ce que c'est.
Olumide
12

Si vous développez piloté par les tests (TDD), vous testerez vos méthodes privées.

Jader Dias
la source
2
Vous extrairez les méthodes privées lors de la refactorisation agiletips.blogspot.com/2008/11/…
Josh Johnson
4
Ce n'est pas vrai, vous testez vos méthodes publiques et une fois les tests réussis, vous extrayez le code de vos méthodes publiques en méthodes privées durant l'étape de "nettoyage". Tester des méthodes privées est une mauvaise idée car cela rend la modification de l'implémentation plus difficile (et si un jour vous voulez changer la façon dont vous faites quelque chose, vous devriez pouvoir le changer et exécuter tous vos tests et si votre nouvelle façon de faire le chose est correcte, ils devraient passer, je ne voudrais pas avoir à changer tous mes tests privés pour cela).
Tesseract
1
@Tesseract, si je pouvais voter plus d'une fois avec votre commentaire. "... vous devriez pouvoir le changer et exécuter tous vos tests et si votre nouvelle façon de faire les choses est correcte, ils devraient réussir" C'EST l'un des principaux avantages des tests unitaires. Ils vous permettent de refaçonner en toute confiance. Vous pouvez changer complètement le fonctionnement privé interne de votre classe et (sans réécrire tous vos tests unitaires) avoir confiance que vous n'avez rien cassé car tous vos tests unitaires (existants) (sur votre interface publique) passent toujours.
Lee
Pas d'accord, voir ma réponse ci
Matt Messersmith
11

Je ne suis pas un expert dans ce domaine, mais les tests unitaires devraient tester le comportement, pas la mise en œuvre. Les méthodes privées font strictement partie de la mise en œuvre, donc IMHO ne doit pas être testé.

maxbog
la source
Où l'implémentation est-elle alors testée? Si certaines fonctionnalités utilisent la mise en cache, s'agit-il alors d'un détail d'implémentation et la mise en cache n'est pas testée?
Dirk Herrmann
11

Nous testons les méthodes privées par inférence, ce qui signifie que nous recherchons une couverture de test de classe totale d'au moins 95%, mais que nos tests ne font appel qu'à des méthodes publiques ou internes. Pour obtenir la couverture, nous devons effectuer plusieurs appels au public / internes en fonction des différents scénarios qui peuvent se produire. Cela rend nos tests plus attentifs à l'objectif du code qu'ils testent.

La réponse de Trumpi au message que vous avez lié est la meilleure.

Tom Carr
la source
9

Les tests unitaires sont, je crois, destinés à tester des méthodes publiques. Vos méthodes publiques utilisent vos méthodes privées, donc indirectement elles sont également testées.

scubabbl
la source
7

Je réfléchis à ce problème depuis un certain temps, en particulier en essayant le TDD.

Je suis tombé sur deux articles qui, je pense, résolvent ce problème de manière suffisamment approfondie dans le cas de TDD.

  1. Test de méthodes privées, TDD et refactoring piloté par les tests
  2. Le développement piloté par les tests n'est pas un test

En résumé:

  • Lorsque vous utilisez des techniques de développement (conception) pilotées par les tests, les méthodes privées ne doivent apparaître que pendant le processus de refactorisation du code déjà fonctionnel et testé.

  • De par la nature même du processus, tout élément de fonctionnalité d'implémentation simple extrait d'une fonction soigneusement testée sera lui-même testé (c'est-à-dire la couverture des tests indirects).

Pour moi, il semble assez clair qu'au début, la plupart des méthodes de codage seront des fonctions de niveau supérieur car elles encapsulent / décrivent la conception.

Par conséquent, ces méthodes seront publiques et les tester sera assez facile.

Les méthodes privées viendront plus tard une fois que tout fonctionne bien et nous refactorisons pour des raisons de lisibilité et de propreté .

dkinzer
la source
6

Comme cité ci-dessus, "Si vous ne testez pas vos méthodes privées, comment savez-vous qu'elles ne se casseront pas?"

C'est un problème majeur. L'un des grands points des tests unitaires est de savoir où, quand et comment quelque chose s'est cassé dès que possible. Diminuant ainsi une quantité importante d'efforts de développement et d'assurance qualité. Si tout ce qui est testé est le public, alors vous n'avez pas une couverture et une délimitation honnêtes des internes de la classe.

J'ai trouvé l'une des meilleures façons de le faire, il suffit d'ajouter la référence de test au projet et de placer les tests dans une classe parallèle aux méthodes privées. Mettez la logique de construction appropriée afin que les tests ne soient pas intégrés dans le projet final.

Ensuite, vous avez tous les avantages de tester ces méthodes et vous pouvez trouver des problèmes en quelques secondes par rapport aux minutes ou aux heures.

Donc en résumé, oui, testez à l'unité vos méthodes privées.

Adron
la source
2
Je ne suis pas d'accord. "Si vous ne testez pas vos méthodes privées, comment savez-vous qu'elles ne casseront pas?" : Je le sais car si mes méthodes privées sont brisées, les tests qui testent mes méthodes publiques qui s'appuient sur ces méthodes privées échoueront. Je ne veux pas avoir à changer mes tests chaque fois que je change d'avis sur la façon d'implémenter les méthodes publiques. Je pense aussi que l'intérêt principal des tests unitaires n'est pas de savoir spécifiquement quelle ligne de code est défectueuse, mais plutôt de vous permettre d'être plus ou moins confiant que vous n'avez rien cassé lors des modifications (aux méthodes privées).
Tesseract
6

Tu ne devrais pas . Si vos méthodes privées ont suffisamment de complexité à tester, vous devez les mettre dans une autre classe. Gardez une cohésion élevée , une classe ne devrait avoir qu'un seul but. L'interface publique de classe devrait être suffisante.

fernandezdavid7
la source
3

Si vous ne testez pas vos méthodes privées, comment savez-vous qu'elles ne casseront pas?

Billy Jo
la source
19
En écrivant à travers des tests de vos méthodes publiques.
scubabbl
3
Ces méthodes privées seraient appelées par les méthodes publiques de la classe. Il suffit donc de tester les méthodes publiques qui appellent les méthodes privées.
JOP
1
Si vos méthodes publiques fonctionnent correctement, alors, évidemment, les méthodes privées auxquelles elles accèdent fonctionnent correctement.
17 de 26
Si les tests de vos méthodes publiques échouent, vous savez instantanément que quelque chose n'est pas correct à un niveau inférieur dans votre objet / composant / etc.
Rob
3
C'est vraiment bien, cependant, de savoir que c'est une fonction interne et pas seulement les fonctions externes qui se sont cassées (ou inversement que les fonctions internes sont très bien et que vous pouvez vous concentrer sur l'extérieur).
Adam Davis,
2

Cela dépend évidemment de la langue. Dans le passé avec c ++, j'ai déclaré que la classe testing était une classe amie. Malheureusement, cela nécessite que votre code de production connaisse la classe de test.

dvorak
la source
5
Le mot-clé ami me rend triste.
Rob
Ce n'est pas un problème si la classe testing est implémentée dans un autre projet. L'important est que le code de production ne fasse pas référence à la classe de test.
Olumide
2

Je comprends le point de vue où les méthodes privées sont considérées comme des détails d'implémentation et n'ont donc pas à être testées. Et je m'en tiendrais à cette règle si nous devions nous développer en dehors de l'objet uniquement. Mais nous, sommes-nous une sorte de développeurs restreints qui ne développent qu'en dehors des objets, en appelant uniquement leurs méthodes publiques? Ou développons-nous également cet objet? Comme nous ne sommes pas tenus de programmer des objets extérieurs, nous devrons probablement appeler ces méthodes privées dans de nouvelles méthodes publiques que nous développons. Ne serait-il pas formidable de savoir que la méthode privée résiste contre toute attente?

Je sais que certaines personnes pourraient répondre que si nous développons une autre méthode publique dans cet objet, alors celle-ci devrait être testée et c'est tout (la méthode privée pourrait continuer à vivre sans test). Mais cela est également vrai pour toutes les méthodes publiques d'un objet: lors du développement d'une application web, toutes les méthodes publiques d'un objet sont appelées à partir des méthodes des contrôleurs et peuvent donc être considérées comme des détails d'implémentation pour les contrôleurs.

Alors pourquoi faisons-nous des tests unitaires sur les objets? Parce que c'est vraiment difficile, pour ne pas dire impossible d'être sûr que nous testons les méthodes des contrôleurs avec l'entrée appropriée qui déclenchera toutes les branches du code sous-jacent. En d'autres termes, plus nous sommes élevés dans la pile, plus il est difficile de tester tout le comportement. Et il en va de même pour les méthodes privées.

Pour moi, la frontière entre les méthodes privées et publiques est un critère psychologique en matière de tests. Les critères qui m'importent le plus sont:

  • la méthode est-elle appelée plusieurs fois depuis différents endroits?
  • la méthode est-elle suffisamment sophistiquée pour nécessiter des tests?
Olivier Pichon
la source
1

Si je trouve que la méthode privée est énorme ou complexe ou suffisamment importante pour nécessiter ses propres tests, je la mets simplement dans une autre classe et la rend publique là-bas (objet méthode). Ensuite, je peux facilement tester la méthode auparavant privée mais maintenant publique qui vit maintenant dans sa propre classe.

Andy
la source
1

Je ne comprends jamais le concept de test unitaire mais maintenant je sais quel est l'objectif.

Un test unitaire n'est pas un test complet . Donc, ce n'est pas un remplacement pour le contrôle qualité et le test manuel. Le concept de TDD dans cet aspect est faux car vous ne pouvez pas tout tester, y compris les méthodes privées mais aussi les méthodes qui utilisent des ressources (en particulier des ressources que nous n'avons pas de contrôle). TDD fonde toute sa qualité sur quelque chose qu'il n'a pas pu atteindre.

Un test unitaire est plus un test de pivot. Vous marquez un pivot arbitraire et le résultat du pivot doit rester le même.

magallanes
la source
1

Public vs privé n'est pas une distinction utile pour ce que les API doivent appeler à partir de vos tests, pas plus que la méthode ou la classe. La plupart des unités testables sont visibles dans un contexte, mais cachées dans d'autres.

Ce qui compte, c'est la couverture et les coûts. Vous devez minimiser les coûts tout en atteignant les objectifs de couverture de votre projet (ligne, branche, chemin, bloc, méthode, classe, classe d'équivalence, cas d'utilisation ... quoi que l'équipe décide).

Utilisez donc des outils pour assurer la couverture et concevez vos tests pour réduire les coûts (à court et à long terme ).

Ne faites pas les tests plus chers que nécessaire. S'il est le moins cher de ne tester que les points d'entrée publics, faites-le. S'il est moins cher de tester des méthodes privées, faites-le.

Au fur et à mesure que vous devenez plus expérimenté, vous deviendrez de mieux en mieux prédire quand cela vaut la peine d'être refactorisé pour éviter les coûts à long terme de la maintenance des tests.

tkruse
la source
0

Si la méthode est suffisamment importante / assez complexe, je la fais généralement "protégée" et la teste. Certaines méthodes resteront privées et testées implicitement dans le cadre de tests unitaires pour les méthodes publiques / protégées.


la source
1
@VisibleForTesting est une annotation pour cela. Je ne détendrais pas l'encapsulation pour les tests, plutôt utiliser dp4j.com
simpatico
0

Je vois que beaucoup de gens sont dans la même ligne de pensée: tester au niveau public. mais n'est-ce pas ce que fait notre équipe QA? Ils testent l'entrée et la sortie attendue. Si en tant que développeurs, nous testons uniquement les méthodes publiques, nous refaisons simplement le travail de QA et n'ajoutons aucune valeur par des «tests unitaires».

aemorales1
la source
La tendance actuelle est de réduire ou de ne pas avoir d'équipe QA. Ces tests unitaires deviennent les tests automatisés qui s'exécutent chaque fois qu'un ingénieur envoie le code sur la branche principale. Même avec l'assurance qualité, il est impossible de tester l'ensemble de l'application aussi rapidement que les tests automatisés.
Patrick Desjardins
0

La réponse à "Dois-je tester des méthodes privées?" est parfois". En règle générale, vous devez tester par rapport à l'interface de vos classes.

  • L'une des raisons est que vous n'avez pas besoin d'une double couverture pour une fonctionnalité.
  • Une autre raison est que si vous modifiez des méthodes privées, vous devrez mettre à jour chaque test pour elles, même si l'interface de votre objet n'a pas changé du tout.

Voici un exemple:

class Thing
  def some_string
    one + two
  end

  private 

  def one
    'aaaa'
  end

  def two
    'bbbb'
  end

end


class RefactoredThing
def some_string
    one + one_a + two + two_b
  end

  private 

  def one
    'aa'
  end

  def one_a
    'aa'
  end

  def two
    'bb'
  end

  def two_b
    'bb'
  end
end

En RefactoredThingvous maintenant 5 tests, dont 2 vous deviez mettre à jour pour refactoring, mais la fonctionnalité de votre objet n'a pas vraiment changé. Disons donc que les choses sont plus complexes que cela et que vous avez une méthode qui définit l'ordre de la sortie, comme:

def some_string_positioner
  if some case
  elsif other case
  elsif other case
  elsif other case
  else one more case
  end
end

Cela ne devrait pas être exécuté par un utilisateur externe, mais votre classe d'encapsulation peut être trop lourde pour exécuter autant de logique à travers elle encore et encore. Dans ce cas, vous préférez peut-être extraire cela dans une classe distincte, donner à cette classe une interface et la tester.

Et enfin, disons que votre objet principal est super lourd, et la méthode est assez petite et vous devez vraiment vous assurer que la sortie est correcte. Vous pensez: "Je dois tester cette méthode privée!". Avez-vous que vous pourriez peut-être rendre votre objet plus léger en passant une partie du travail lourd comme paramètre d'initialisation? Ensuite, vous pouvez passer quelque chose de plus léger et tester contre cela.

unflores
la source
0

Non Vous ne devriez pas tester les méthodes privées pourquoi? De plus, le framework de simulation populaire tel que Mockito ne prend pas en charge le test des méthodes privées.

cammando
la source
0

Un point principal est

Si nous testons pour garantir l'exactitude de la logique, et qu'une méthode privée porte une logique, nous devons la tester. N'est-ce pas? Alors pourquoi allons-nous sauter cela?

La rédaction de tests basés sur la visibilité des méthodes est une idée totalement hors de propos.

inversement

D'un autre côté, appeler une méthode privée en dehors de la classe d'origine est un problème majeur. Et il y a aussi des limitations pour se moquer d'une méthode privée dans certains outils de simulation. (Ex: Mockito )

Bien qu'il existe des outils comme Power Mock qui prennent en charge cela, c'est une opération dangereuse. La raison en est qu'il doit pirater la JVM pour y parvenir.

Une solution à ce problème peut être (si vous voulez écrire des cas de test pour des méthodes privées)

Déclarez ces méthodes privées protégées . Mais cela peut ne pas convenir à plusieurs situations.

Supun Wijerathne
la source
0

Il ne s'agit pas seulement de méthodes ou de fonctions publiques ou privées, il s'agit de détails d'implémentation. Les fonctions privées ne sont qu'un aspect des détails de mise en œuvre.

Le test unitaire, après tout, est une approche de test en boîte blanche. Par exemple, quiconque utilise l'analyse de couverture pour identifier les parties du code qui ont été négligées jusqu'à présent dans les tests, entre dans les détails de l'implémentation.

A) Oui, vous devriez tester les détails de mise en œuvre:

Pensez à une fonction de tri qui, pour des raisons de performances, utilise une implémentation privée de BubbleSort s'il y a jusqu'à 10 éléments, et une implémentation privée d'une approche de tri différente (disons, heapsort) s'il y a plus de 10 éléments. L'API publique est celle d'une fonction de tri. Cependant, votre suite de tests utilise mieux les connaissances selon lesquelles il existe en fait deux algorithmes de tri.

Dans cet exemple, vous pourriez sûrement effectuer les tests sur l'API publique. Cela nécessiterait, cependant, d'avoir un certain nombre de cas de test qui exécutent la fonction de tri avec plus de 10 éléments, de sorte que l'algorithme de tri sélectif soit suffisamment bien testé. L'existence de tels cas de test seuls indique que la suite de tests est connectée aux détails d'implémentation de la fonction.

Si les détails d'implémentation de la fonction de tri changent, peut-être de la manière dont la limite entre les deux algorithmes de tri est décalée ou que le tri par tas est remplacé par mergesort ou autre: les tests existants continueront de fonctionner. Leur valeur est néanmoins discutable, et ils doivent probablement être retravaillés pour mieux tester la fonction de tri modifiée. En d'autres termes, il y aura un effort de maintenance malgré le fait que les tests étaient sur l'API publique.

B) Comment tester les détails de l'implémentation

Une raison pour laquelle de nombreuses personnes soutiennent qu'il ne faut pas tester les fonctions privées ou les détails d'implémentation est que les détails d'implémentation sont plus susceptibles de changer. Cette probabilité plus élevée de changement au moins est l'une des raisons pour lesquelles les détails d'implémentation sont masqués derrière les interfaces.

Supposons maintenant que l'implémentation derrière l'interface contient des parties privées plus grandes pour lesquelles des tests individuels sur l'interface interne peuvent être une option. Certaines personnes soutiennent que ces parties ne doivent pas être testées lorsqu'elles sont privées, elles doivent être transformées en quelque chose de public. Une fois rendu public, le test unitaire de ce code serait OK.

C'est intéressant: bien que l'interface soit interne, elle était susceptible de changer, étant un détail d'implémentation. Prendre la même interface, la rendre publique fait une transformation magique, à savoir la transformer en une interface moins susceptible de changer. De toute évidence, il y a une faille dans cette argumentation.

Mais il y a néanmoins une part de vérité derrière cela: lors du test des détails d'implémentation, en particulier à l'aide d'interfaces internes, il faut s'efforcer d'utiliser des interfaces susceptibles de rester stables. Cependant, le fait de savoir si une interface est susceptible d'être stable n'est pas simplement décidable selon qu'elle est publique ou privée. Dans les projets du monde dans lesquels je travaille depuis un certain temps, les interfaces publiques changent aussi assez souvent et de nombreuses interfaces privées sont restées intactes depuis des lustres.

Pourtant, c'est une bonne règle de base d'utiliser la "porte d'entrée en premier" (voir http://xunitpatterns.com/Principles%20of%20Test%20Automation.html ). Mais gardez à l'esprit qu'elle est appelée "porte d'entrée en premier" et non "porte d'entrée uniquement".

C) Résumé

Testez également les détails d'implémentation. Préférez les tests sur des interfaces stables (publiques ou privées). Si les détails de l'implémentation changent, les tests sur l'API publique doivent également être révisés. Transformer quelque chose de privé en public ne change pas comme par magie sa stabilité.

Dirk Herrmann
la source
0

Oui, dans la mesure du possible, vous devez tester des méthodes privées. Pourquoi? Pour éviter une explosion inutile de l'espace d'état des cas de test qui finissent par se retrouver implicitement à tester de manière répétée les mêmes fonctions privées sur les mêmes entrées. Expliquons pourquoi avec un exemple.

Prenons l'exemple légèrement artificiel suivant. Supposons que nous voulons exposer publiquement une fonction qui prend 3 entiers et retourne vrai si et seulement si ces 3 entiers sont tous premiers. Nous pourrions l'implémenter comme ceci:

public bool allPrime(int a, int b, int c)
{
  return andAll(isPrime(a), isPrime(b), isPrime(c))
}

private bool andAll(bool... boolArray)
{
  foreach (bool b in boolArray)
  {
    if(b == false) return false;
  }
  return true;
}

private bool isPrime(int x){
  //Implementation to go here. Sorry if you were expecting a prime sieve.
}

Maintenant, si nous devions adopter l'approche stricte selon laquelle seules les fonctions publiques devraient être testées, nous ne serions autorisés qu'à tester allPrimeet non isPrimeou andAll.

En tant que testeur, nous pourrions être intéressés à cinq possibilités pour chaque argument: < 0, = 0, = 1, prime > 1, not prime > 1. Mais pour être approfondi, nous devons également voir comment chaque combinaison d'arguments se joue ensemble. Donc, c'est 5*5*5= 125 cas de test dont nous aurions besoin pour tester minutieusement cette fonction, selon nos intuitions.

En revanche, si nous étions autorisés à tester les fonctions privées, nous pourrions couvrir autant de terrain avec moins de cas de test. Nous n'aurions besoin que de 5 cas de test isPrimepour tester au même niveau que notre intuition précédente. Et par l' hypothèse de petite portée proposée par Daniel Jackson, nous n'aurions besoin que de tester la andAllfonction jusqu'à une petite longueur, par exemple 3 ou 4. Ce serait au plus 16 tests supplémentaires. Donc 21 tests au total. Au lieu de 125. Bien sûr, nous voudrions probablement effectuer quelques tests allPrime, mais nous ne nous sentirions pas obligés de couvrir de manière exhaustive toutes les 125 combinaisons de scénarios d'entrée dont nous avons dit que nous nous soucions de nous. Juste quelques chemins heureux.

Un exemple artificiel, bien sûr, mais il était nécessaire pour une démonstration claire. Et le modèle s'étend aux vrais logiciels. Les fonctions privées sont généralement les blocs de construction de niveau le plus bas, et sont donc souvent combinées ensemble pour produire une logique de niveau supérieur. Ce qui signifie à des niveaux plus élevés, nous avons plus de répétitions des choses de niveau inférieur en raison des diverses combinaisons.

Colm Bhandal
la source
Tout d'abord, vous n'avez pas à tester de telles combinaisons avec des fonctions pures comme vous l'avez montré. Les appels à isPrimesont vraiment indépendants, donc tester aveuglément chaque combinaison est assez inutile. Deuxièmement, le marquage d'une fonction pure appelée isPrimeprivée viole tellement de règles de conception que je ne sais même pas par où commencer. isPrimedevrait très clairement être une fonction publique. Cela étant dit, je comprends ce que vous dites, quel que soit cet exemple extrêmement médiocre. Cependant , il est construit au large de la prémisse que vous auriez voulez faire des tests de combinaison, lorsque dans les systèmes logiciels réels , cela est rarement une bonne idée.
Matt Messersmith
Matt oui l'exemple n'est pas idéal je vais vous donner ça. Mais le principe devrait être évident.
Colm Bhandal
La prémisse n'est pas exactement que vous voudriez faire des tests de combinaison. C'est ce que vous auriez à faire, si vous vous limitiez à tester uniquement les pièces publiques du puzzle. Il y a des cas où vous voudriez rendre une fonction pure privée pour adhérer aux principes d'encapsulation appropriés. Et cette fonction purement privée pourrait être utilisée par des fonctions publiques. De manière combinatoire, avec peut-être d'autres fonctions privées pures. Dans ce cas, suivant le dogme selon lequel vous ne testerez pas le privé, vous seriez obligé de faire des tests combinés sur la fonction publique plutôt que de faire des tests modulaires des composants privés.
Colm Bhandal
0

Vous pouvez également rendre votre méthode package-privée, c'est-à-dire par défaut et vous devriez pouvoir la tester à l'unité, sauf si elle doit être privée.

Yogesh
la source