510 votes

Comment tester en unité les méthodes privées ?

Je suis en train de construire une bibliothèque de classes qui aura des méthodes publiques et privées. Je veux être en mesure de tester les méthodes privées (surtout pendant le développement, mais cela pourrait aussi être utile pour une refonte future).

Quelle est la manière correcte de procéder ?

3 votes

J'ai peut-être raté quelque chose, ou peut-être que c'est juste que cette question est, eh bien... pre-historic en termes d'années Internet, mais le test unitaire des méthodes privées est maintenant à la fois facile et direct, avec Visual Studio produisant les classes d'accès nécessaires lorsque cela est nécessaire et pré-remplissant la logique des tests avec des extraits très proches de ce que l'on peut souhaiter pour des tests fonctionnels simples. Voir par exemple. msdn.microsoft.com/fr/us/library/ms184807%28VS.90%29.aspx

5 votes

Cela semble être une copie conforme de stackoverflow.com/questions/34571/ .

0 votes

L'auteur de la question n'utilise peut-être pas Visual Studio

361voto

Jeroen Heijmans Points 3038

Si vous voulez tester en unité une méthode privée, il y a peut-être un problème. Les tests unitaires sont (généralement) destinés à tester l'interface d'une classe, c'est-à-dire ses méthodes publiques (et protégées). Vous pouvez bien sûr "bidouiller" une solution à ce problème (ne serait-ce qu'en rendant les méthodes publiques), mais vous pouvez aussi y réfléchir :

  1. Si la méthode que vous souhaitez tester en vaut vraiment la peine, il peut être intéressant de la déplacer dans sa propre classe.
  2. Ajoutez des tests supplémentaires aux méthodes publiques qui appellent la méthode privée, afin de tester la fonctionnalité de cette dernière. (Comme les commentateurs l'ont indiqué, vous ne devriez faire cela que si la fonctionnalité de ces méthodes privées fait réellement partie de l'interface publique. Si elles exécutent en fait des fonctions qui sont cachées à l'utilisateur (c'est-à-dire au test unitaire), c'est probablement mauvais).

44 votes

L'option 2 oblige les tests unitaires à connaître l'implémentation sous-jacente de la fonction. Je n'aime pas faire cela. Je pense généralement que les tests unitaires devraient tester la fonction sans rien supposer de l'implémentation.

17 votes

L'inconvénient de tester l'implémentation est que les tests seront fragiles et se briseront si vous introduisez des changements dans l'implémentation. Et ceci n'est pas souhaitable car le remaniement est aussi important que l'écriture des tests dans le TDD.

32 votes

Eh bien, les tests sont censés de se casser si vous changez l'implémentation. TDD signifierait changer les tests d'abord.

127voto

TcKs Points 13249

Si vous utilisez .net, vous devez utiliser l'option InternalsVisibleToAttribute .

89 votes

Beurk. Ceci est compilé dans vos assemblages.

16 votes

@Jay - ne pourrait-on pas utiliser #if DEBUG autour de la InternalsVisibleTo pour qu'il ne s'applique pas au code de version ?

21 votes

@Mike, vous pourriez, mais alors vous ne pouvez exécuter des tests unitaires que sur le code de débogage, pas sur le code de publication. Comme le code Release est optimisé, vous pourriez voir un comportement différent et des délais différents. Dans un code multithread, cela signifie que vos tests unitaires ne détecteront pas correctement les conditions de course. Il est préférable d'utiliser la réflexion via la suggestion de @AmazedSaint ci-dessous ou d'utiliser le PrivateObject/PrivateType intégré. Cela vous permet de voir les objets privés dans les builds de la version, en supposant que votre harnais de test fonctionne en toute confiance (ce que fait MSTest en local).

122voto

Seven Points 1604

Il n'est pas forcément utile de tester les 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 fournit deux mécanismes pour cela :

Accesseurs

  • Allez dans le code source de la définition de la classe
  • Cliquez avec le bouton droit de la souris sur le nom de la classe
  • Choisissez "Créer un accesseur privé".
  • Choisissez le projet dans lequel l'accesseur doit être créé. => Vous obtiendrez une nouvelle classe avec le nom foo_accessor. Cette classe sera générée dynamiquement lors de la compilation et fournit tous les membres publics disponibles.

Cependant, le mécanisme est parfois un peu difficile à manier lorsqu'il s'agit de modifier l'interface de la classe d'origine. C'est pourquoi, la plupart du temps, j'évite de l'utiliser.

Classe PrivateObject L'autre moyen est d'utiliser Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject.

// Wrap an already existing instance
PrivateObject accessor = new PrivateObject( objectInstanceToBeWrapped );

// Retrieve a private field
MyReturnType accessiblePrivateField = (MyReturnType) accessor.GetField( "privateFieldName" );

// Call a private method
accessor.Invoke( "PrivateMethodName", new Object[] {/* ... */} );

3 votes

Comment invoquer des méthodes statiques privées ?

20 votes

Les accesseurs privés sont déprécié dans Visual Studio 2012 .

3 votes

La méthode de l'accesseur pour tester les méthodes privées est dépréciée à partir de VS 2011. blogs.msdn.com/b/visualstudioalm/archive/2012/03/08/

90voto

Darrell Plank Points 605

Je ne suis pas d'accord avec la philosophie "vous ne devriez vous intéresser qu'aux tests de l'interface externe". C'est un peu comme si l'on disait qu'un atelier de réparation de voitures ne devait faire des tests que 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 procède à un remaniement, il se peut que je doive changer certains des tests, mais à moins qu'il ne s'agisse d'un remaniement massif, je ne devrai en changer que quelques-uns et le fait que les autres tests internes (inchangés) fonctionnent toujours est un excellent indicateur du succès du remaniement.

Vous pouvez essayer de couvrir tous les cas internes en utilisant uniquement l'interface publique et, théoriquement, il est possible de tester chaque méthode interne (ou du moins toutes celles qui comptent) entièrement en utilisant l'interface publique, mais vous risquez de devoir vous mettre sur la tête pour y parvenir 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 censés tester peut être difficile ou impossible à discerner. Le fait d'avoir des tests pointus et individuels qui garantissent que la machinerie interne fonctionne correctement vaut bien les changements de test mineurs qui surviennent avec le remaniement - du moins, c'est ce que j'ai constaté. Si vous devez apporter d'énormes changements à vos tests à chaque refactoring, alors peut-être que cela n'a pas de sens, mais dans ce cas, vous devriez peut-être repenser entièrement votre conception. Une bonne conception devrait être suffisamment flexible pour permettre la plupart des changements sans avoir à procéder à des remaniements massifs.

21 votes

J'ai bien peur de ne toujours pas être d'accord avec vous. Traiter chaque composant comme une boîte noire permet d'interchanger les modules sans problème. Si vous avez un FooService qui a à voir avec X tout ce qui vous importe, c'est qu'elle le fasse effectivement. X sur demande. Comment si c'est le cas, ça ne devrait pas avoir d'importance. S'il y a des problèmes dans la classe qui ne sont pas perceptibles à travers l'interface (peu probable), c'est toujours un problème valide. FooService . Si c'est un problème que es visible à travers l'interface, un test sur les membres publics devrait le détecter. L'idée générale est que tant que la roue tourne correctement, elle peut être utilisée comme une roue.

6 votes

Une approche générale est que si votre logique interne est suffisamment compliquée pour nécessiter des tests unitaires, elle doit peut-être être extraite dans une sorte de classe d'aide avec une interface publique qui peut être testée par des unités. Ensuite, votre classe "parent" peut simplement utiliser cette classe d'aide, et tout le monde peut être testé de manière unitaire de manière appropriée.

12 votes

@Basic : La logique de cette réponse est complètement fausse. Le cas classique où vous avez besoin d'une méthode privée est lorsque vous avez besoin qu'une partie du code soit réutilisée par les méthodes publiques. Vous mettez ce code dans une méthode PrivMethod. Cette méthode ne devrait pas être exposée au public, mais a besoin d'être testée pour s'assurer que les méthodes publiques, qui s'appuient sur PrivMethod, peuvent vraiment s'y fier.

52voto

Jason Jackson Points 11563

Dans les rares cas où j'ai voulu tester des fonctions privées, je les ai généralement modifiées pour qu'elles soient protégées, puis j'ai écrit une sous-classe avec une fonction d'enveloppe publique.

La classe :

...

protected void APrivateFunction()
{
    ...
}

...

Sous-classe pour les tests :

...

[Test]
public void TestAPrivateFunction()
{
    APrivateFunction();
    //or whatever testing code you want here
}

...

2 votes

Vous pouvez même placer cette classe enfant dans votre fichier de test unitaire au lieu d'encombrer la vraie classe. +1 pour la ruse.

0 votes

Je place toujours tout le code lié aux tests dans le projet des tests unitaires si possible. C'était juste du pseudo-code.

3 votes

Cette fonction n'est pas privée, elle est protégée, le résultat net ... vous avez rendu votre code moins sûr / exposé aux types de fonctionnalités privées des enfants.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X