Je construis une bibliothèque de classe qui aura des méthodes publiques et privées. Je veux pouvoir tester de manière unitaire les méthodes privées (principalement pendant le développement, mais cela pourrait également être utile pour une refactorisation future).
Quelle est la bonne façon de procéder?
.net
unit-testing
tdd
private
Eric Labashosky
la source
la source
pre-historic
en termes d'années Internet, mais les tests unitaires des méthodes privées sont maintenant à la fois simples et directs, Visual Studio produisant les classes d'accesseurs nécessaires en cas de besoin et pré-remplir la logique des tests avec des extraits de code sacrément proches de ce que l'on peut souhaiter pour des tests fonctionnels simples. Voir par exemple. msdn.microsoft.com/en-us/library/ms184807%28VS.90%29.aspxRéponses:
Si vous utilisez .net, vous devez utiliser InternalsVisibleToAttribute .
la source
#if DEBUG
l'InternalsVisibleTo
attribut pour qu'il ne s'applique pas au code de version?#if RELEASE_TEST
autourInternalsVisibleTo
comme Mike le suggère, et à faire une copie de la configuration de votre version qui définitRELEASE_TEST
. Vous pouvez tester votre code de version avec des optimisations, mais lorsque vous construisez réellement pour la version, vos tests seront omis.Si vous souhaitez tester unitaire une méthode privée, quelque chose ne va peut-être pas. Les tests unitaires sont (en général) destinés à tester l'interface d'une classe, c'est-à-dire ses méthodes publiques (et protégées). Vous pouvez bien sûr "pirater" une solution à cela (même si ce n'est qu'en rendant les méthodes publiques), mais vous pouvez également envisager:
la source
Il peut ne pas être utile de tester des méthodes privées. Cependant, j'aime aussi parfois appeler des méthodes privées à partir de méthodes de test. La plupart du temps afin d'éviter la duplication de code pour la génération de données de test ...
Microsoft propose deux mécanismes pour cela:
Accesseurs
Cependant, le mécanisme est parfois un peu insoluble quand il s'agit de changements de l'interface de la classe d'origine. Donc, la plupart du temps, j'évite de l'utiliser.
Classe PrivateObject L'autre méthode consiste à utiliser Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject
la source
Je ne suis pas d'accord avec la philosophie "vous ne devriez être intéressé qu'à tester l'interface externe". C'est un peu comme dire qu'un atelier de réparation automobile ne devrait avoir que des tests pour voir si les roues tournent. Oui, en fin de compte, je suis intéressé par le comportement externe, mais j'aime que mes propres tests internes privés soient un peu plus spécifiques et précis. Oui, si je refactorise, je devrai peut-être changer certains des tests, mais à moins que ce soit un refactorisé massif, je n'aurai qu'à en changer quelques-uns et le fait que les autres tests internes (inchangés) fonctionnent toujours est un excellent indicateur que la refactorisation a réussi.
Vous pouvez essayer de couvrir tous les cas internes en utilisant uniquement l'interface publique et théoriquement, il est possible de tester toutes les méthodes internes (ou au moins toutes celles qui comptent) entièrement en utilisant l'interface publique, mais vous devrez peut-être vous retrouver debout sur la tête pour atteindre cela et la connexion entre les cas de test exécutés via l'interface publique et la partie interne de la solution qu'ils sont conçus pour tester peut être difficile, voire impossible à discerner. Cela dit, les tests individuels qui garantissent le bon fonctionnement de la machine interne valent bien les changements de test mineurs qui résultent de la refactorisation - du moins, c'est mon expérience. Si vous devez apporter d'énormes changements à vos tests pour chaque refactoring, alors cela n'a peut-être pas de sens, mais dans ce cas, vous devriez peut-être repenser entièrement votre conception.
la source
FooService
à faireX
, tout ce dont vous devez vous soucier, c'est qu'il le fait effectivement surX
demande. La façon dont cela fonctionne ne devrait pas avoir d'importance. S'il y a des problèmes dans la classe qui ne sont pas discernables via l'interface (peu probable), c'est quand même valideFooService
. Si c'est un problème qui est visible à travers l'interface, un test sur les membres du public devrait le détecter. Le point devrait être que tant que la roue tourne correctement, elle peut être utilisée comme roue.PrivMethod
, un test surPubMethod
lequel les appelsPrivMethod
devraient l'exposer? Que se passe-t-il lorsque vous changez votreSimpleSmtpService
en unGmailService
? Tout d'un coup, vos tests privés pointent sur du code qui n'existe plus ou qui fonctionne peut-être différemment et qui échouerait, même si l'application peut fonctionner parfaitement comme prévu. S'il y a un traitement complexe qui s'appliquerait aux deux expéditeurs d'e-mails, peut-être devrait-il l'être dans unEmailProcessor
qui peut être utilisé par les deux et testé séparément?Dans les rares cas où j'ai voulu tester des fonctions privées, je les ai généralement modifiées pour être protégées à la place, et j'ai écrit une sous-classe avec une fonction wrapper publique.
La classe:
Sous-classe pour les tests:
la source
Je pense qu'une question plus fondamentale devrait être posée: pourquoi essayez-vous de tester la méthode privée en premier lieu. C'est une odeur de code que vous essayez de tester la méthode privée via l'interface publique de cette classe alors que cette méthode est privée pour une raison car c'est un détail d'implémentation. On ne devrait se préoccuper que du comportement de l'interface publique et non de la façon dont elle est implémentée sous les couvertures.
Si je veux tester le comportement de la méthode privée, en utilisant des refactorings communs, je peux extraire son code dans une autre classe (peut-être avec une visibilité au niveau du package alors assurez-vous qu'elle ne fait pas partie d'une API publique). Je peux ensuite tester son comportement de manière isolée.
Le produit de la refactorisation signifie que la méthode privée est maintenant une classe distincte qui est devenue un collaborateur de la classe d'origine. Son comportement sera bien compris grâce à ses propres tests unitaires.
Je peux ensuite me moquer de son comportement lorsque j'essaie de tester la classe d'origine afin de pouvoir me concentrer sur le comportement de l'interface publique de cette classe plutôt que d'avoir à tester une explosion combinatoire de l'interface publique et le comportement de toutes ses méthodes privées .
Je vois cela comme conduire une voiture. Lorsque je conduis une voiture, je ne conduis pas avec le capot relevé, je peux donc voir que le moteur fonctionne. Je me fie à l'interface fournie par la voiture, à savoir le compte-tours et le compteur de vitesse pour savoir que le moteur fonctionne. Je compte sur le fait que la voiture bouge réellement lorsque j'appuie sur la pédale d'accélérateur. Si je veux tester le moteur, je peux le vérifier isolément. :RÉ
Bien sûr, tester directement des méthodes privées peut être un dernier recours si vous avez une application héritée, mais je préférerais que le code hérité soit remanié pour permettre de meilleurs tests. Michael Feathers a écrit un excellent livre sur ce sujet. http://www.amazon.co.uk/Working-Effectively-Legacy-Robert-Martin/dp/0131177052
la source
Les types privés, les internes et les membres privés le sont pour une raison quelconque, et souvent vous ne voulez pas les toucher directement. Et si vous le faites, il est probable que vous vous cassiez plus tard, car il n'y a aucune garantie que les gars qui ont créé ces assemblys conserveront les implémentations privées / internes en tant que telles.
Mais, parfois, lorsque je fais des hacks / explorations d'assemblages compilés ou tiers, j'ai moi-même fini par vouloir initialiser une classe privée ou une classe avec un constructeur privé ou interne. Ou, parfois, lorsque je traite des bibliothèques héritées précompilées que je ne peux pas changer - je finis par écrire des tests par rapport à une méthode privée.
Ainsi est née AccessPrivateWrapper - http://amazedsaint.blogspot.com/2010/05/accessprivatewrapper-c-40-dynamic.html - c'est une classe d'emballage rapide qui rendra le travail facile en utilisant les fonctionnalités dynamiques et la réflexion C # 4.0.
Vous pouvez créer des types internes / privés comme
la source
Eh bien, vous pouvez tester la méthode privée de deux manières
vous pouvez créer une instance de
PrivateObject
classe la syntaxe est la suivanteVous pouvez utiliser la réflexion.
la source
PrivateClass
et l'utiliser. stackoverflow.com/questions/9122708/…J'ai également utilisé la méthode InternalsVisibleToAttribute. Il convient également de mentionner que si vous ne vous sentez pas à l'aise de rendre vos méthodes précédemment privées internes pour y parvenir, alors elles ne devraient peut-être pas faire l'objet de tests unitaires directs de toute façon.
Après tout, vous testez le comportement de votre classe, plutôt que son implémentation spécifique - vous pouvez changer la dernière sans changer la première et vos tests devraient toujours réussir.
la source
Il existe 2 types de méthodes privées. Méthodes privées statiques et méthodes privées non statiques (méthodes d'instance). Les 2 articles suivants expliquent comment tester les méthodes privées à l'aide d'exemples.
la source
MS Test a une belle fonctionnalité intégrée qui rend les membres privés et les méthodes disponibles dans le projet en créant un fichier appelé VSCodeGenAccessors
Avec des classes dérivées de BaseAccessor
tel que
la source
Sur CodeProject, il y a un article qui discute brièvement les avantages et les inconvénients des tests de méthodes privées. Il fournit ensuite un code de réflexion pour accéder aux méthodes privées (similaire au code fourni par Marcus ci-dessus.) Le seul problème que j'ai trouvé avec l'exemple est que le code ne prend pas en compte les méthodes surchargées.
Vous pouvez trouver l'article ici:
http://www.codeproject.com/KB/cs/testnonpublicmembers.aspx
la source
Déclarez-les
internal
, puis utilisez leInternalsVisibleToAttribute
pour permettre à votre ensemble de tests unitaires de les voir.la source
J'ai tendance à ne pas utiliser les directives du compilateur car elles encombrent rapidement les choses. Une façon de l'atténuer si vous en avez vraiment besoin est de les placer dans une classe partielle et de laisser votre build ignorer ce fichier .cs lors de la création de la version de production.
la source
Vous ne devriez pas tester les méthodes privées de votre code en premier lieu. Vous devriez tester l '«interface publique» ou l'API, les choses publiques de vos classes. Les API sont toutes les méthodes publiques que vous exposez aux appelants externes.
La raison en est qu'une fois que vous commencez à tester les méthodes privées et internes de votre classe, vous couplez l'implémentation de votre classe (les choses privées) à vos tests. Cela signifie que lorsque vous décidez de modifier vos détails d'implémentation, vous devrez également modifier vos tests.
Pour cette raison, vous devez éviter d'utiliser InternalsVisibleToAtrribute.
Voici un excellent discours d'Ian Cooper qui couvre ce sujet: Ian Cooper: TDD, où tout s'est-il mal passé?
la source
Parfois, il peut être bon de tester des déclarations privées. Fondamentalement, un compilateur n'a qu'une seule méthode publique: Compile (chaîne outputFileName, params chaîne [] sourceSFileNames). Je suis sûr que vous comprenez qu'il serait difficile de tester une telle méthode sans tester chaque déclaration "cachée"!
C'est pourquoi nous avons créé Visual T #: pour faciliter les tests. C'est un langage de programmation .NET gratuit (compatible C # v2.0).
Nous avons ajouté l'opérateur '.-'. Il se comporte simplement comme "." , sauf que vous pouvez également accéder à toute déclaration cachée de vos tests sans rien changer dans votre projet testé.
Jetez un œil à notre site Web: téléchargez- le gratuitement .
la source
Je suis surpris que personne ne l'ait encore dit, mais une solution que j'ai employée est de créer une méthode statique à l'intérieur de la classe pour se tester. Cela vous donne accès à tout ce qui est public et privé pour tester.
De plus, dans un langage de script (avec des capacités OO, comme Python, Ruby et PHP), vous pouvez faire le test du fichier lui-même lors de son exécution. Belle façon rapide de vous assurer que vos modifications n'ont rien cassé. Cela fait évidemment une solution évolutive pour tester toutes vos classes: exécutez-les toutes. (vous pouvez également le faire dans d'autres langues avec un void main qui exécute toujours ses tests également).
la source
Je veux créer ici un exemple de code clair que vous pouvez utiliser sur n'importe quelle classe dans laquelle vous souhaitez tester une méthode privée.
Dans votre classe de cas de test, incluez simplement ces méthodes, puis utilisez-les comme indiqué.
$ this -> _ callMethod ('_ someFunctionName', tableau (param1, param2, param3));
Émettez simplement les paramètres dans l'ordre dans lequel ils apparaissent dans la fonction privée d'origine
la source
Pour tous ceux qui veulent utiliser des méthodes privées sans les tracas. Cela fonctionne avec n'importe quel framework de test unitaire n'utilisant rien d'autre qu'une bonne vieille réflexion.
Ensuite, dans vos tests réels, vous pouvez faire quelque chose comme ceci:
la source
MbUnit a obtenu un joli emballage pour ce appelé Reflector.
Vous pouvez également définir et obtenir des valeurs à partir des propriétés
Concernant le "test privé" je suis d'accord que .. dans le monde parfait. il est inutile de faire des tests unitaires privés. Mais dans le monde réel, vous pourriez finir par vouloir écrire des tests privés au lieu de refactoriser le code.
la source
Reflector
a été remplacé par le plus puissantMirror
de Gallio / MbUnit v3.2. ( gallio.org/wiki/doku.php?id=mbunit:mirror )Voici un bon article sur les tests unitaires des méthodes privées. Mais je ne sais pas quoi de mieux, pour vous faire une application spécialement conçue pour les tests (c'est comme créer des tests pour les tests uniquement) ou utiliser la réflexion pour les tests. Je suis sûr que la plupart d'entre nous choisiront la deuxième voie.
la source
À mon avis, vous ne devriez tester que l'API publique de votre classe.
Rendre une méthode publique, afin de la tester unitaire, rompt l'encapsulation et expose les détails de l'implémentation.
Une bonne API publique résout un objectif immédiat du code client et résout complètement cet objectif.
la source
J'utilise PrivateObject classe. Mais comme mentionné précédemment, il vaut mieux éviter de tester des méthodes privées.
la source
"CC" est le compilateur de ligne de commande sur le système que j'utilise.
-Dfoo=bar
fait l'équivalent de#define foo bar
. Donc, cette option de compilation transforme efficacement tous les trucs privés en public.la source
Voici un exemple, d'abord la signature de la méthode:
Voici le test:
la source
Un moyen de le faire est d'avoir votre méthode
protected
et d'écrire un montage de test qui hérite de votre classe à tester. De cette façon, vous ne tournez pas votre méthodepublic
, mais vous activez le test.la source
1) Si vous avez un code hérité, la seule façon de tester des méthodes privées est par réflexion.
2) S'il s'agit d'un nouveau code, vous disposez des options suivantes:
Je préfère la méthode d'annotation, la plus simple et la moins compliquée. Le seul problème est que nous avons accru la visibilité, ce qui, je pense, n'est pas une grande préoccupation. Nous devrions toujours coder pour l'interface, donc si nous avons une interface MyService et une implémentation MyServiceImpl, nous pouvons alors avoir les classes de test correspondantes qui sont MyServiceTest (méthodes d'interface de test) et MyServiceImplTest (tester les méthodes privées). Tous les clients devraient de toute façon utiliser l'interface, donc d'une certaine manière, même si la visibilité de la méthode privée a été augmentée, cela ne devrait pas vraiment avoir d'importance.
la source
Vous pouvez également le déclarer public ou interne (avec InternalsVisibleToAttribute) lors de la construction en mode débogage:
Il gonfle le code, mais il sera
private
dans une version.la source
Vous pouvez générer la méthode de test pour la méthode privée à partir de Visual studio 2008. Lorsque vous créez un test unitaire pour une méthode privée, un dossier Références de test est ajouté à votre projet de test et un accesseur est ajouté à ce dossier. L'accesseur est également mentionné dans la logique de la méthode de test unitaire. Cet accesseur permet à votre test unitaire d'appeler des méthodes privées dans le code que vous testez. Pour plus de détails, consultez
http://msdn.microsoft.com/en-us/library/bb385974.aspx
la source
Notez également que InternalsVisibleToAtrribute a une exigence que votre assembly soit nommé fort , ce qui crée son propre ensemble de problèmes si vous travaillez dans une solution qui n'avait pas eu cette exigence auparavant. J'utilise l'accesseur pour tester des méthodes privées. Voir cette question pour un exemple de cela.
la source
InternalsVisibleToAttribute
ne nécessite pas que vos assemblys soient fortement nommés. Je l'utilise actuellement sur un projet où ce n'est pas le cas.