J'ai lu ce post 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 appelées de l'extérieur de l'objet. Est-ce que vous testez des méthodes privées? Devrais-je toujours les tester?
Je ne teste pas les méthodes privées. Une méthode privée est un détail d'implémentation qui doit être masqué par les utilisateurs de la classe. Tester des méthodes privées rompt l’encapsulation.
Si je trouve que la méthode privée est énorme, complexe ou suffisamment importante pour nécessiter ses propres tests, je la mets simplement dans une autre classe et la rend publique ici ( Method Object ). Ensuite, je peux facilement tester la méthode auparavant privée, mais maintenant publique, qui vit maintenant dans sa propre classe.
Quel est le but de tester?
Jusqu'à présent, la majorité des réponses disent que les méthodes privées sont des détails d'implémentation qui importent peu (ou du moins ne devraient pas l'être) tant que l'interface publique est bien testée et fonctionne. C'est tout à fait correct si votre seul objectif est de garantir le bon fonctionnement de l'interface publique .
Personnellement, mon principal usage pour les tests de code est de veiller à ce que les modifications de code futures 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 de la même manière que l'interface publique (si ce n'est plus!) Poursuit 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 réside dans l’utilisation de C par B, par C ou par les deux?
Tester des méthodes privées ajoute également de la valeur dans les cas où la couverture de test de l'interface publique est incomplète. Bien que nous souhaitions généralement éviter cette situation, les tests unitaires d’efficacité reposent à la fois sur les tests de recherche de bogues et sur les 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, ce qui crée 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.
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 casser une encapsulation pour le plaisir de test (ou comme maman disait, "n'exposez pas vos soldats!"). Plus du temps, vous devriez pouvoir tester une classe en exerçant son méthodes publiques. Si des fonctionnalités importantes sont masquées derrière un accès privé ou protégé, cela pourrait être un signe avant-coureur que il y a une autre classe qui lutte pour sortir.
Mais parfois, je ne peux pas m'empêcher de tester des méthodes privées car cela me donne l'assurance de construire un programme complètement robuste.
Je me sens un peu obligé de tester des fonctions privées car je suis de plus en plus attentif à l'une de nos dernières recommandations en matière d'assurance qualité dans notre projet:
Pas plus de 10 in complexité cyclomatique par fonction.
Maintenant, l’application de cette politique a pour effet secondaire de diviser un grand nombre de mes très grandes fonctions publiques en plusieurs fonctions plus ciblées et mieux nommées private.
La fonction publique est toujours là (bien sûr) mais est essentiellement réduite à l’appel de toutes ces "sous-fonctions" privées
C’est vraiment cool, car le callstack est maintenant beaucoup plus facile à lire (au lieu d’un bogue dans une fonction importante, j’ai un bogue dans une sous-sous-fonction avec le nom des fonctions précédentes dans le callstack pour m'aider à comprendre 'comment j'y suis arrivé')
Cependant, il semble maintenant plus facile de tester directement ces fonctions private, et de laisser le test de la fonction publique de grande taille à une sorte de test «d'intégration» lorsqu'un scénario doit être traité.
Juste mes 2 cents.
Oui, je teste des fonctions privées, car bien qu'elles soient testées par vos méthodes publiques, il est agréable d'utiliser TDD (Test Driven Design) pour tester la plus petite partie de l'application. Mais les fonctions privées ne sont pas accessibles lorsque vous vous trouvez dans la classe de votre 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 par l'intermédiaire de nos méthodes publiques. De plus, nous ne souhaitons 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 une conception pilotée par les tests (TDD). Cela signifie que nous tombons parfois sur une fonctionnalité privée que nous souhaitons 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 qu'il y a 3 solutions:
1. Vous pouvez tester vos privés avec vos méthodes publiques
Avantages
Désavantages
2. Si le privé est si important, alors c'est peut-être un code pour créer une nouvelle classe séparée pour lui
Avantages
Désavantages
3. Modifiez le modificateur d'accès en (final) protected
Avantages
Désavantages
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);
}
}
Ainsi, notre unité de test peut maintenant appeler test_sleepWithSuspect pour tester notre ancienne fonction privée.
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 dirigés.
Une fois que vous avez écrit quelques tests unitaires solides pour un objet, vous ne souhaitez plus revenir en arrière et les modifier simplement parce que la mise en œuvre derrière l'interface a été modifiée. Dans cette situation, vous avez gâché la cohérence de vos tests unitaires.
Si la méthode privée est bien définie (c’est-à-dire qu’elle a une fonction testable et qu’elle n’est pas censée changer dans le temps), alors oui. Je teste tout ce qui est testable lorsque cela a du sens.
Par exemple, une bibliothèque de chiffrement peut masquer le fait qu’elle bloque le chiffrement avec une méthode privée qui ne chiffre que 8 octets à la fois. J'écrirais un test unitaire pour cela - il n'est pas censé changer, même s'il est caché, et si cela ne fonctionne pas (en raison d'améliorations futures des performances, par exemple), alors je veux savoir que c'est la fonction privée qui a rompu, pas seulement que l'une des fonctions publiques a éclaté.
Cela accélère le débogage plus tard.
-Adam
Si votre méthode privée n'est pas testée en appelant vos méthodes publiques, que fait-elle alors? Je parle de privé non protégé ou d'ami.
Si vous développez TDD (Test Driven Test), vous testerez vos méthodes privées.
Je ne suis pas un expert dans ce domaine, mais les tests unitaires doivent tester le comportement et non la mise en œuvre. Les méthodes privées font strictement partie de la mise en œuvre, donc IMHO ne doit pas être testé.
Nous testons les méthodes privées par inférence, ce qui signifie que nous recherchons une couverture totale des tests de classe d’au moins 95%, mais que nos tests n’appellent que des méthodes publiques ou internes. Pour obtenir la couverture, nous devons faire plusieurs appels au public/aux internes en fonction des différents scénarios pouvant 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.
Je pense que les tests unitaires sont destinés à tester des méthodes publiques. Vos méthodes publiques utilisent vos méthodes privées, elles sont donc indirectement également testées.
Cela fait un moment que je médite sur cette question, en particulier en m'essayant à la TDD.
J'ai rencontré deux articles qui, je pense, abordent ce problème de manière suffisamment approfondie dans le cas du TDD.
En résumé:
Lors de l'utilisation de techniques de développement (conception) basées sur des tests, les méthodes privées ne doivent apparaître que lors du processus de re-factorisation d'un code déjà actif et testé.
De par la nature même du processus, toute fonctionnalité d’implémentation simple extraite d’une fonction testée de manière approfondie sera elle-même testée (couverture de test indirect).
Pour moi, il semble assez clair qu'au début du codage, la plupart des méthodes sont des fonctions de niveau supérieur, car elles encapsulent/décrivent la conception.
Par conséquent, ces méthodes seront publiques et leur test sera assez facile.
Les méthodes privées viendront plus tard une fois que tout fonctionnera bien et que nous restructurerons pour le bien de lisibilité et propreté.
Comme cité ci-dessus, "Si vous ne testez pas vos méthodes privées, comment savez-vous qu'elles ne vont pas casser?"
C'est un problème majeur. L'un des gros points des tests unitaires est de savoir où, quand et comment quelque chose s'est cassé dès que possible. Ce qui diminue considérablement les efforts de développement et d’assurance qualité. Si tout ce qui est testé est le public, alors vous n'avez pas une couverture honnête et une délimitation des internes de la classe.
J'ai trouvé que l'une des meilleures façons de procéder consiste simplement à ajouter la référence de test au projet et à placer les tests dans une classe parallèle aux méthodes privées. Indiquez la logique de construction appropriée afin que les tests ne soient pas intégrés au projet final.
Vous avez alors tous les avantages de faire tester ces méthodes et vous pouvez trouver des problèmes en quelques secondes ou quelques heures.
Donc, en résumé, oui, testez vos méthodes privées.
Tu ne devrais pas . Si vos méthodes privées ont suffisamment de complexité pour être testées, vous devriez les placer dans une autre classe. Gardez une cohésion élevée , une classe ne devrait avoir qu'un seul objectif. L'interface publique de classe devrait suffire.
Si vous ne testez pas vos méthodes privées, comment savez-vous qu'elles ne casseront pas?
C'est évidemment dépendant de la langue. Dans le passé avec c ++, j'ai déclaré que la classe testing était une classe friend. Malheureusement, cela nécessite que votre code de production connaisse la classe de test.
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 pas besoin d'être testées. Et je resterais avec cette règle si nous devions développer en dehors de l'objet uniquement. Mais nous, sommes-nous une sorte de développeurs restreints qui ne développent que des objets, 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 à toute éventualité?
Je sais que certaines personnes pourraient répondre que si nous développons une autre méthode publique pour cet objet, celle-ci devrait alors être testée et c'est tout (la méthode privée pourrait continuer à vivre sans test). Mais ceci 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 de méthodes de contrôleurs et peuvent donc être considérées comme des détails d'implémentation pour les contrôleurs.
Alors, pourquoi sommes-nous en train de tester des objets? Parce qu’il est très difficile, voire impossible, de tester 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 dans la pile, plus il est difficile de tester tous les comportements. Il en va de même pour les méthodes privées.
Pour moi, la frontière entre méthodes privées et méthodes publiques est un critère psychologique en matière de test. Les critères qui comptent le plus pour moi sont:
Si je trouve que la méthode privée est énorme, complexe ou suffisamment importante pour nécessiter ses propres tests, je la mets simplement dans une autre classe et la rend publique là-bas (Method Object). Ensuite, je peux facilement tester la méthode auparavant privée mais maintenant publique qui vit maintenant dans sa propre classe.
La réponse à "Devrais-je tester des méthodes privées?" est parfois". En règle générale, vous devriez tester l'interface de vos classes.
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
Dans RefactoredThing
, vous avez maintenant 5 tests, dont 2 que vous avez dû mettre à jour pour le refactoring, mais la fonctionnalité de votre objet n'a pas vraiment changé. Alors disons que les choses sont plus complexes que cela et que vous avez une méthode qui définit l’ordre de la sortie, telle que:
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 extérieur, 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éreriez peut-être extraire ceci dans une classe séparée, lui donner une interface et le tester.
Et enfin, disons que votre objet principal est super lourd, que la méthode est assez petite et que vous devez vraiment vous assurer que le résultat est correct. Vous pensez: "Je dois tester cette méthode privée!". Pensez-vous que vous pouvez peut-être alléger votre objet en définissant une partie du travail lourd en tant que paramètre d'initialisation? Ensuite, vous pouvez passer quelque chose de plus léger et tester contre cela.
Il ne s'agit pas uniquement de méthodes ou de fonctions publiques ou privées, il concerne également les détails de la mise en œuvre. Les fonctions privées ne sont qu'un aspect des détails de la mise en œuvre.
Après tout, le test unitaire est une méthode de test en boîte blanche. Par exemple, quiconque utilise l'analyse de couverture pour identifier des parties du code qui ont été négligées jusqu'à présent dans les tests, va dans les détails de la mise en œuvre.
A) Oui, vous devriez tester les détails de la mise en œuvre:
Imaginez une fonction de tri qui, pour des raisons de performances, utilise une implémentation privée de BubbleSort s’il existe 10 éléments au maximum, et une implémentation privée d’une approche de tri différente (disons, pile) s’il ya plus de 10 éléments. L'API publique est celle d'une fonction de tri. Cependant, votre suite de tests tire mieux parti de la connaissance selon laquelle deux algorithmes de tri sont utilisés.
Dans cet exemple, vous pouvez sûrement effectuer les tests sur l'API publique. Toutefois, cela nécessiterait de disposer d’un certain nombre de cas de test exécutant la fonction de tri avec plus de 10 éléments, de sorte que l’algorithme d’empilement soit suffisamment testé. L'existence de tels cas de test à elle seule 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 même manière que la limite entre les deux algorithmes de tri est décalée ou que heapsort est remplacé par mergesort ou autre: les tests existants continueront à fonctionner. Leur valeur est néanmoins discutable, et ils devront probablement être retravaillés pour mieux tester la fonction de tri modifiée. En d’autres termes, des efforts de maintenance seront nécessaires malgré le fait que les tests ont été effectués sur l’API publique.
B) Comment tester les détails de la mise en œuvre
L'une des raisons pour lesquelles de nombreuses personnes soutiennent qu'il ne faut pas tester les fonctions privées ou les détails de la mise en œuvre est que les détails de la mise en œuvre sont plus susceptibles de changer. Cette probabilité plus élevée de changement est au moins l’une des raisons pour lesquelles les détails de la mise en œuvre sont cachés derrière les interfaces.
Supposons maintenant que l'implémentation derrière l'interface contienne des parties privées plus grandes pour lesquelles des tests individuels sur l'interface interne pourraient être une option. Certaines personnes soutiennent que ces pièces ne doivent pas être testées lorsqu'elles sont privées, elles doivent être transformées en quelque chose de public. Une fois public, tester ce code par unité serait OK.
C'est intéressant: l'interface était interne, mais elle était susceptible de changer car il s'agissait d'un détail d'implémentation. Prendre la même interface, la rendre publique, effectue 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 tout de même une vérité derrière cela: lors du test des détails de la mise en œuvre, en particulier à l'aide d'interfaces internes, il faut s'efforcer d'utiliser des interfaces susceptibles de rester stables. Le fait de savoir si une interface est susceptible d'être stable n'est toutefois pas simplement décisif selon qu'il est public ou privé. Dans les projets du monde dans lequel je travaille depuis un certain temps, les interfaces publiques changent également assez souvent et de nombreuses interfaces privées sont restées intactes depuis des siècles.
Néanmoins, c’est une bonne règle empirique d’utiliser la «porte d’entrée en premier» (voir http://xunitpatterns.com/Principles%20of%20Test%20Automation.html ). Mais gardez à l'esprit que cela s'appelle "porte d'entrée d'abord" et non "porte d'entrée seulement".
C) Résumé
Testez également les détails de la mise en œuvre. Préférez les tests sur des interfaces stables (publiques ou privées). Si les détails de l'implémentation changent, les tests de l'API publique doivent également être révisés. Transformer quelque chose de privé en public ne change pas comme par magie sa stabilité.
Vous pouvez également rendre votre méthode package-private i.e. default et vous devriez pouvoir la tester à l'unité, sauf si elle doit être privée.
Un point principal est
Si nous testons pour nous assurer de l'exactitude de la logique et qu'une méthode privée comporte une logique, nous devrions la tester. N'est-ce pas? Alors, pourquoi allons-nous sauter cela?
Ecrire des tests basés sur la visibilité des méthodes est une idée complètement hors de propos.
Inversement
D'autre part, l'appel d'une méthode privée en dehors de la classe d'origine est un problème majeur. Et aussi, il existe des limites à la méthode privée pour certains outils de simulation. (Ex: Mockito )
Bien qu'il existe des outils comme Power Mock qui le supporte, c'est une opération dangereuse. La raison en est qu'il faut pirater la JVM pour y parvenir.
Un moyen de contourner le problème est le suivant: (si vous souhaitez écrire des scénarios de test pour des méthodes privées)
Déclarez ces méthodes private comme protected . Mais cela peut ne pas convenir dans plusieurs situations.
Si la méthode est suffisamment significative/complexe, je la mettrai généralement "protégée" et la testerai. Certaines méthodes seront laissées privées et testées implicitement dans le cadre de tests unitaires pour les méthodes publiques/protégées.
Oui, vous devriez tester les méthodes privées, dans la mesure du possible. Pourquoi? Pour éviter une explosion de l'espace d'état } inutile des cas de test qui finissent par tester implicitement les mêmes fonctions privées à plusieurs reprises sur les mêmes entrées. Expliquons pourquoi avec un exemple.
Considérez l'exemple suivant légèrement artificiel. Supposons que nous voulions exposer publiquement une fonction qui prend 3 entiers et renvoie true 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 adoptions une approche stricte voulant que seules les fonctions publiques soient testées, nous ne pourrions tester que allPrime
et non pas isPrime
ou andAll
.
En tant que testeur, nous pourrions être intéressés par cinq possibilités pour chaque argument: < 0
, = 0
, = 1
, prime > 1
, not prime > 1
. Mais pour être approfondi, il faudrait également voir comment chaque combinaison d'arguments joue ensemble. Donc, c’est donc 5*5*5
= 125 cas de test pour lesquels nous aurions besoin de tester minutieusement cette fonction, selon nos intuitions.
D'autre part, si nous étions autorisés à tester les fonctions privées, nous pourrions couvrir autant de terrain avec moins de cas de test. Nous aurions besoin de seulement 5 cas de test pour tester isPrime
au même niveau que notre intuition précédente. Et, selon la hypothèse de petite portée proposée par Daniel Jackson, il suffit de tester la fonction andAll
jusqu'à une petite longueur, par exemple. 3 ou 4. Ce qui serait au plus 16 autres tests. Donc, 21 tests au total. Au lieu de 125. Bien sûr, nous voudrions probablement exécuter un peu tests sur allPrime
, mais nous ne nous sentirions pas obligés de couvrir de manière exhaustive les 125 combinaisons de scénarios de saisie qui nous intéressaient. Juste quelques chemins heureux.
Un exemple artificiel, certes, mais il était nécessaire pour une démonstration claire. Et le modèle s'étend au logiciel réel. Les fonctions privées sont généralement les blocs de construction de niveau inférieur et sont donc souvent combinées pour donner une logique de niveau supérieur. Ce qui signifie à des niveaux plus élevés, nous avons plus de répétitions de la substance de niveau inférieur en raison des diverses combinaisons.
Je vois beaucoup de gens dans le même ordre d'idées: tester au niveau public. mais n'est-ce pas ce que notre équipe d'assurance qualité fait? Ils testent l'entrée et la sortie attendue. Si, en tant que développeurs, nous ne testons que les méthodes publiques, nous ne faisons que rétablir le travail du contrôle qualité et ne pas ajouter de valeur en procédant à des "tests unitaires".
Public contre privé n'est pas une distinction utile pour savoir quel apis appeler depuis vos tests, ni méthode contre classe. La plupart des unités testables sont visibles dans un contexte mais cachées dans un autre.
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, etc.).
Utilisez donc des outils pour assurer la couverture et concevez vos tests de manière à réduire les coûts (à court et à long terme). Parfois, vous pouvez facilement obtenir toute la couverture dont vous avez besoin en ne testant que le principal point d'entrée de l'application. Plus souvent, vous réduirez les coûts liés à la couverture en effectuant des tests à différents points d’entrée internes. Ce concept ne s'arrête pas à la visibilité de la méthode.
Souvent, une bonne conception de module rend la réalisation du test moins onéreuse contre des points d’entrée déclarés publics, mais cela n’est pas garanti.
Privé contre public n'a pas d'importance.
Ne rendez pas les tests plus coûteux en testant directement toutes les méthodes privées et évitez les appels plus coûteux en évitant d'appeler directement certaines méthodes.
Non, vous ne devriez pas tester les méthodes privées pourquoi? et de plus, le framework de moquage populaire tel que Mockito ne fournit pas de support pour tester des méthodes privées.
Je n'ai jamais compris le concept de test unitaire, mais je sais maintenant quel est son objectif.
Un test unitaire n’est pas un test complet . Donc, ce n'est pas un remplacement pour l'AQ et le test manuel. Le concept de TDD sous cet aspect est faux car vous ne pouvez pas tout tester, y compris les méthodes privées, mais également les méthodes qui utilisent des ressources (en particulier des ressources que nous n'avons pas le contrôle). TDD considère que toute sa qualité est impossible à atteindre.
Un test unitaire est plus un test de pivot Vous marquez un pivot arbitraire et le résultat du pivot devrait rester le même.