Comment testez-vous les méthodes privées?

Je construis une bibliothèque de classes qui aura des méthodes publiques et privées. Je veux être capable de tester les méthodes privées (surtout lors du développement, mais cela pourrait aussi être utile pour le futur refactoring).

Quelle est la bonne façon de faire cela?

Si vous utilisez .net, vous devez utiliser l’ InternalsVisibleToAtsortingbute .

Si vous voulez tester une méthode privée, il se peut que quelque chose ne fonctionne pas. 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 (même en rendant les méthodes publiques), mais vous pouvez également envisager:

  1. Si la méthode que vous souhaitez tester vaut vraiment la peine d’être testée, il peut être utile de la déplacer dans sa propre classe.
  2. Ajoutez d’autres tests aux méthodes publiques qui appellent la méthode privée, en testant la fonctionnalité de la méthode privée. (Comme les commentateurs l’ont indiqué, vous ne devriez le faire que si la fonctionnalité de ces méthodes privées fait vraiment partie de l’interface publique. Si elles exécutent des fonctions cachées à l’utilisateur (c’est-à-dire le test unitaire), cela est probablement mauvais).

Il pourrait 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 pour éviter la duplication de code pour la génération de données de test …

Microsoft fournit deux mécanismes pour cela:

Les accesseurs

  • Aller au code source de la définition de classe
  • Clic droit sur le nom de la classe
  • Choisissez “Créer un accesseur privé”
  • Choisissez le projet dans lequel l’accesseur doit être créé => Vous allez vous retrouver avec une nouvelle classe avec le nom foo_accessor. Cette classe sera générée dynamicment lors de la compilation et privide tous les membres publics disponibles.

Cependant, le mécanisme est parfois un peu compliqué en ce qui concerne les modifications de l’interface de la classe d’origine. Donc, la plupart du temps, j’évite de l’utiliser.

Classe PrivateObject L’autre moyen consiste à utiliser Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject

 // Wrap an already existing instance PrivateObject accessor = new PrivateObject( objectInstanceToBeWrapped ); // Resortingeve a private field MyReturnType accessiblePrivateField = (MyReturnType) accessor.GetField( "privateFieldName" ); // Call a private method accessor.Invoke( "PrivateMethodName", new Object[] {/* ... */} ); 

Je ne suis pas d’accord avec la philosophie “vous ne devriez être intéressé que par le test de 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, au final, je suis intéressé par le comportement externe, mais j’aime mes propres tests internes privés pour être un peu plus précis et pertinents. Oui, si je refactore, je devrai peut-être changer certains tests, mais à moins que ce ne soit un refactor massif, je n’en changerai que quelques-uns et le fait que les autres tests internes (inchangés) fonctionnent toujours est un excellent indicateur le refactoring 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 du moins toutes les questions importantes) en utilisant l’interface publique, mais vous devrez peut-être Ceci et la connexion entre les scénarios de test exécutés via l’interface publique et la partie interne de la solution qu’ils sont conçus pour tester peuvent être difficiles ou impossibles à discerner. Ayant souligné, les tests individuels qui garantissent le bon fonctionnement de la machine interne valent bien les modifications de test mineures apscopes par le refactoring – du moins, c’est mon expérience. Si vous devez apporter d’importants changements à vos tests pour chaque refactoring, alors peut-être que cela n’a aucun sens, mais dans ce cas, vous devriez peut-être repenser entièrement votre conception. Un bon design doit être suffisamment flexible pour permettre la plupart des changements sans modifications majeures.

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:

 ... protected void APrivateFunction() { ... } ... 

Sous-classe pour les tests:

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

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 son implémentation 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 afin de ne pas faire partie d’une API publique). Je peux ensuite tester son comportement isolément.

Le produit du refactoring 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 alors me moquer de son comportement lorsque j’essaie de tester la classe d’origine pour pouvoir ensuite me concentrer sur le test du comportement de l’interface publique de cette classe plutôt que de devoir 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. Quand je conduis une voiture, je ne conduis pas avec le capot, donc je peux voir que le moteur fonctionne. Je me fie à l’interface fournie par la voiture, à savoir le compte-tours et l’indicateur de vitesse pour savoir si le moteur fonctionne. Je compte sur le fait que la voiture se déplace effectivement lorsque j’appuie sur la pédale d’accélérateur. Si je veux tester le moteur, je peux faire des vérifications isolées. :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 existant soit refait pour permettre de meilleurs tests. Michael Feathers a écrit un excellent livre sur ce sujet même. http://www.amazon.co.uk/Working-Effectively-Legacy-Robert-Martin/dp/0131177052

Les types privés, les internes et les membres privés le sont pour une raison ou une autre, et souvent vous ne voulez pas les toucher directement. Et si vous le faites, il y a de fortes chances que vous vous arrêtiez plus tard, car rien ne garantit que les créateurs de ces assemblées conserveront les implémentations privées / internes en tant que telles.

Mais, parfois, lorsque je fais du piratage / exploration 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 ne peux pas changer les anciennes bibliothèques pré-compilées, je finis par écrire des tests sur une méthode privée.

Ainsi, AccessPrivateWrapper – http://amazedsaint.blogspot.com/2010/05/accessprivatewrapper-c-40-dynamic.html – est une classe d’emballage rapide qui facilite le travail en utilisant les fonctions dynamics et la reflection C # 4.0.

Vous pouvez créer des types internes / privés comme

  //Note that the wrapper is dynamic dynamic wrapper = AccessPrivateWrapper.FromType (typeof(SomeKnownClass).Assembly,"ClassWithPrivateConstructor"); //Access the private members wrapper.PrivateMethodInPrivateClass(); 

J’ai également utilisé la méthode InternalsVisibleToAtsortingbute. Il convient de mentionner également que si vous ne vous sentez pas à l’aise pour rendre vos méthodes auparavant internes pour y parvenir, elles ne devraient peut-être pas faire l’object de tests unitaires directs.

Après tout, vous testez le comportement de votre classe, plutôt que son implémentation spécifique – vous pouvez changer ce dernier sans changer le premier et vos tests doivent toujours passer.

Eh bien, vous pouvez tester la méthode privée de deux manières

  1. vous pouvez créer une instance de la classe PrivateObject la syntaxe est la suivante

     PrivateObject obj= new PrivateObject(PrivateClass); //now with this obj you can call the private method of PrivateCalss. obj.PrivateMethod("Parameters"); 
  2. Vous pouvez utiliser la reflection.

     PrivateClass obj = new PrivateClass(); // Class containing private obj Type t = typeof(PrivateClass); var x = t.InvokeMember("PrivateFunc", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, null, obj, new object[] { 5 }); 

MS Test a une fonctionnalité intéressante qui rend les membres privés et les méthodes disponibles dans le projet en créant un fichier appelé VSCodeGenAccessors

 [System.Diagnostics.DebuggerStepThrough()] [System.CodeDom.Comstackr.GeneratedCodeAtsortingbute("Microsoft.VisualStudio.TestTools.UnitTestGeneration", "1.0.0.0")] internal class BaseAccessor { protected Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject m_privateObject; protected BaseAccessor(object target, Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType type) { m_privateObject = new Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject(target, type); } protected BaseAccessor(Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType type) : this(null, type) { } internal virtual object Target { get { return m_privateObject.Target; } } public override ssortingng ToSsortingng() { return this.Target.ToSsortingng(); } public override bool Equals(object obj) { if (typeof(BaseAccessor).IsInstanceOfType(obj)) { obj = ((BaseAccessor)(obj)).Target; } return this.Target.Equals(obj); } public override int GetHashCode() { return this.Target.GetHashCode(); } } 

Avec des classes dérivées de BaseAccessor

tel que

 [System.Diagnostics.DebuggerStepThrough()] [System.CodeDom.Comstackr.GeneratedCodeAtsortingbute("Microsoft.VisualStudio.TestTools.UnitTestGeneration", "1.0.0.0")] internal class SomeClassAccessor : BaseAccessor { protected static Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType m_privateType = new Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType(typeof(global::Namespace.SomeClass)); internal SomeClassAccessor(global::Namespace.Someclass target) : base(target, m_privateType) { } internal static ssortingng STATIC_STRING { get { ssortingng ret = ((ssortingng)(m_privateType.GetStaticField("STATIC_STRING"))); return ret; } set { m_privateType.SetStaticField("STATIC_STRING", value); } } internal int memberVar { get { int ret = ((int)(m_privateObject.GetField("memberVar"))); return ret; } set { m_privateObject.SetField("memberVar", value); } } internal int PrivateMethodName(int paramName) { object[] args = new object[] { paramName}; int ret = (int)(m_privateObject.Invoke("PrivateMethodName", new System.Type[] { typeof(int)}, args))); return ret; } 

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 avec des exemples.

  1. Méthodes d’essai statiques
  2. Méthodes d’essai non statiques

Je n’ai pas tendance à 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 faire en sorte que votre build ignore ce fichier .cs lors de la création de la version de production.

Dans CodeProject, il existe un article qui traite brièvement des avantages et des inconvénients du test des méthodes privées. Il fournit ensuite un code de reflection 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

Déclarez-les internal , puis utilisez InternalsVisibleToAtsortingbute pour permettre à votre unité de test de les voir.

Parfois, il peut être utile de tester des déclarations privées. Fondamentalement, un compilateur n’a qu’une méthode publique: Comstackr (ssortingng outputFileName, params ssortingng [] 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 comme ‘.’ opérateur, sauf que vous pouvez également accéder à toute déclaration cachée de vos tests sans rien changer à votre projet testé.

Consultez notre site Web: téléchargez- le gratuitement .

Vous ne devriez pas tester les méthodes privées de votre code en premier lieu. Vous devriez tester l’interface publique ou API, les choses publiques de vos classes. L’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 associez l’implémentation de votre classe (les éléments privés) à vos tests. Cela signifie que lorsque vous décidez de modifier les détails de votre implémentation, vous devrez également modifier vos tests.

Pour cette raison, évitez d’utiliser InternalsVisibleToAtrribute.

Voici un excellent exposé de Ian Cooper qui couvre ce sujet: Ian Cooper: TDD, où tout s’est-il mal passé?

MbUnit a obtenu un joli wrapper appelé Reflector.

 Reflector dogReflector = new Reflector(new Dog()); dogReflector.Invoke("DreamAbout", DogDream.Food); 

Vous pouvez également définir et obtenir des valeurs à partir des propriétés

 dogReflector.GetProperty("Age"); 

En ce qui concerne 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 vouloir écrire des tests privés au lieu de refactoriser du code.

Je suis surpris que personne n’ait encore dit cela, mais une solution que j’ai employée consiste à créer une méthode statique à l’intérieur de la classe pour se tester. Cela vous donne access à tout ce qui est public et privé à tester.

De plus, dans un langage de script (avec des capacités OO, telles que Python, Ruby et PHP), vous pouvez faire le test du fichier lui-même lorsqu’il est exécuté. Bon moyen rapide de s’assurer que vos modifications n’ont rien cassé. Cela rend évidemment une solution évolutive pour tester toutes vos classes: il suffit de les exécuter toutes. (Vous pouvez aussi le faire dans d’autres langues avec un main vide qui exécute toujours ses tests).

Je veux créer un exemple de code clair que vous pouvez utiliser sur n’importe quelle classe dans laquelle vous voulez tester une méthode privée.

Dans votre classe de test, incluez simplement ces méthodes et utilisez-les comme indiqué.

  /** * * @var Class_name_of_class_you_want_to_test_private_methods_in * note: the actual class and the private variable to store the * class instance in, should at least be different case so that * they do not get confused in the code. Here the class name is * is upper case while the private instance variable is all lower * case */ private $class_name_of_class_you_want_to_test_private_methods_in; /** * This uses reflection to be able to get private methods to test * @param $methodName * @return ReflectionMethod */ protected static function getMethod($methodName) { $class = new ReflectionClass('Class_name_of_class_you_want_to_test_private_methods_in'); $method = $class->getMethod($methodName); $method->setAccessible(true); return $method; } /** * Uses reflection class to call private methods and get return values. * @param $methodName * @param array $params * @return mixed * * usage: $this->_callMethod('_someFunctionName', array(param1,param2,param3)); * {params are in * order in which they appear in the function declaration} */ protected function _callMethod($methodName, $params=array()) { $method = self::getMethod($methodName); return $method->invokeArgs($this->class_name_of_class_you_want_to_test_private_methods_in, $params); } 

$ this -> _ callMethod (‘_ someFunctionName’, array (param1, param2, param3));

Il suffit d’émettre les parameters dans l’ordre dans lequel ils apparaissent dans la fonction privée d’origine

Pour tous ceux qui veulent utiliser des méthodes privées sans la fasce et le désordre. Cela fonctionne avec n’importe quel framework de test unitaire en utilisant uniquement de bonnes vieilles reflections.

 public class ReflectionTools { // If the class is non-static public static Object InvokePrivate(Object objectUnderTest, ssortingng method, params object[] args) { Type t = objectUnderTest.GetType(); return t.InvokeMember(method, BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static, null, objectUnderTest, args); } // if the class is static public static Object InvokePrivate(Type typeOfObjectUnderTest, ssortingng method, params object[] args) { MemberInfo[] members = typeOfObjectUnderTest.GetMembers(BindingFlags.NonPublic | BindingFlags.Static); foreach(var member in members) { if (member.Name == method) { return typeOfObjectUnderTest.InvokeMember(method, BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.InvokeMethod, null, typeOfObjectUnderTest, args); } } return null; } } 

Then in your actual tests, you can do something like this:

 Assert.AreEqual( ReflectionTools.InvokePrivate( typeof(StaticClassOfMethod), "PrivateMethod"), "Expected Result"); Assert.AreEqual( ReflectionTools.InvokePrivate( new ClassOfMethod(), "PrivateMethod"), "Expected Result"); 
 CC -Dprivate=public 

Here is good article about unit testing of private methods. But I’m not sure what’s better, to make you application designed specially for testing(it’s like creating tests for testing only) or use reflexion for testing. Pretty sure most of us will choose second way.

I use PrivateObject class. But as mentioned previously better to avoid testing private methods.

 Class target = new Class(); PrivateObject obj = new PrivateObject(target); var retVal = obj.Invoke("PrivateMethod"); Assert.AreEqual(retVal); 

You could generate the test method for the private method from Visual studio 2008. When you create a unit test for a private method, a Test References folder is added to your test project and an accessor is added to that folder. The accessor is also referred to in the logic of the unit test method. This accessor allows your unit test to call private methods in the code that you are testing. For details have a look at

http://msdn.microsoft.com/en-us/library/bb385974.aspx

Also note that the InternalsVisibleToAtrribute has a requirement that your assembly be strong named , which creates it’s own set of problems if you’re working in a solution that had not had that requirement before. I use the accessor to test private methods. See this question that for an example of this.

Here’s an example, first the method signature:

 private ssortingng[] SplitInternal() { return Regex.Matches(Format, @"([^/\[\]]|\[[^]]*\])+") .Cast() .Select(m => m.Value) .Where(s => !ssortingng.IsNullOrEmpty(s)) .ToArray(); } 

Here’s the test:

 ///  ///A test for SplitInternal /// [TestMethod()] [DeploymentItem("Git XmlLib vs2008.dll")] public void SplitInternalTest() { ssortingng path = "pair[path/to/@Key={0}]/Items/Item[Name={1}]/Date"; object[] values = new object[] { 2, "Martin" }; XPathSsortingng xp = new XPathSsortingng(path, values); PrivateObject param0 = new PrivateObject(xp); XPathSsortingng_Accessor target = new XPathSsortingng_Accessor(param0); ssortingng[] expected = new ssortingng[] { "pair[path/to/@Key={0}]", "Items", "Item[Name={1}]", "Date" }; ssortingng[] actual; actual = target.SplitInternal(); CollectionAssert.AreEqual(expected, actual); } 

A way to do this is to have your method protected and write a test fixture which inherits your class to be tested. This way, you are nor turning your method public , but you enable the testing.

1) If you have a legacy code then the only way to test private methods is by reflection.

2) If it is new code then you have the following options:

  • Use reflection (to complicated)
  • Write unit test in the same class (makes the production code ugly by having test code also in it)
  • Refactor and make the method public in some kind of util class
  • Use @VisibleForTesting annotation and remove private

I prefer the annotation method, simplest and least complicated. The only issue is that we have increased the visibility which I think is not a big concern. We should always be coding to interface, so if we have an interface MyService and an implementation MyServiceImpl then we can have the corresponding test classes that is MyServiceTest (test interface methods) and MyServiceImplTest (test private methods). All clients should anyway be using the interface so in a way even though the visibility of the private method has been increased it should not really matter.

You could also declare it as public or internal (with InternalsVisibleToAtsortingbute) while building in debug-Mode:

  ///  /// This Method is private. ///  #if DEBUG public #else private #endif static ssortingng MyPrivateMethod() { return "false"; } 

It bloats the code, but it will be private in a release build.