¿Cómo pruebas tus métodos privados?

Estoy construyendo una biblioteca de clases que tendrá algunos métodos públicos y privados. Quiero poder probar los métodos privados (principalmente durante el desarrollo, pero también podría ser útil para futuras refactorizaciones).

¿Cuál es la forma correcta de hacer esto?

Si está utilizando .net, debe usar InternalsVisibleToAttribute .

Si desea probar la unidad de un método privado, algo puede estar mal. Las pruebas unitarias (generalmente hablando) están destinadas a probar la interfaz de una clase, es decir, sus métodos públicos (y protegidos). Por supuesto, puede “piratear” una solución a esto (incluso si solo hace que los métodos sean públicos), pero también puede considerar:

  1. Si realmente vale la pena probar el método que desea probar, puede valer la pena moverlo a su propia clase.
  2. Agregue más pruebas a los métodos públicos que llaman al método privado, probando la funcionalidad del método privado. (Como indicaron los comentaristas, solo debe hacer esto si la funcionalidad de estos métodos privados es realmente parte de la interfaz pública. Si realmente realizan funciones que están ocultas para el usuario (es decir, la prueba unitaria), esto probablemente sea malo).

Puede que no sea útil probar métodos privados. Sin embargo, a veces también me gusta llamar a métodos privados desde métodos de prueba. La mayoría de las veces para evitar la duplicación de código para la generación de datos de prueba …

Microsoft proporciona dos mecanismos para esto:

Accesorios

  • Ir al código fuente de la definición de la clase
  • Haga clic derecho en el nombre de la clase
  • Elija “Crear acceso privado”
  • Elija el proyecto en el que se debe crear el descriptor de acceso => ​​Finalizará con una nueva clase con el nombre foo_accessor. Esta clase se generará dinámicamente durante la comstackción y dará acceso a todos los miembros públicos disponibles.

Sin embargo, el mecanismo es a veces un poco difícil de tratar cuando se trata de cambios en la interfaz de la clase original. Entonces, la mayoría de las veces evito usar esto.

Clase PrivateObject La otra forma es usar 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[] {/* ... */} ); 

No estoy de acuerdo con la filosofía “solo debería estar interesado en probar la interfaz externa”. Es un poco como decir que un taller de reparación de automóviles solo debería tener pruebas para ver si las ruedas giran. Sí, en última instancia, estoy interesado en el comportamiento externo, pero me gustan mis propias pruebas internas privadas para ser un poco más específicas y precisas. Sí, si refactoro, tendré que cambiar algunas de las pruebas, pero a menos que sea un refactor masivo, solo tendré que cambiar unas pocas y el hecho de que las otras pruebas internas (sin modificar) aún funcionen es un gran indicador de que la refactorización ha sido exitosa.

Puede tratar de cubrir todos los casos internos utilizando solo la interfaz pública y, teóricamente, es posible probar por completo cada método interno (o al menos cada uno que sea importante) utilizando la interfaz pública, pero es posible que tenga que terminar parado sobre su cabeza para lograr esto y la conexión entre los casos de prueba que se ejecutan a través de la interfaz pública y la parte interna de la solución que están diseñados para probar puede ser difícil o imposible de discernir. Después de señalar que las pruebas individuales que garantizan que la maquinaria interna funciona correctamente, bien valen los pequeños cambios de prueba que se producen con la refactorización, al menos esa ha sido mi experiencia. Si tiene que hacer grandes cambios en sus pruebas para cada refactorización, tal vez esto no tenga sentido, pero en ese caso, tal vez debería reconsiderar su diseño por completo. Un buen diseño debe ser lo suficientemente flexible como para permitir la mayoría de los cambios sin rediseños masivos.

En los pocos casos en que he querido probar funciones privadas, generalmente las he modificado para protegerlas, y he escrito una subclase con una función de contenedor público.

La clase:

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

Subclase para prueba:

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

Creo que se debe hacer una pregunta más fundamental: ¿por qué intentas probar el método privado en primer lugar? Ese es un olor a código que está tratando de probar el método privado a través de la interfaz pública de esa clase, mientras que ese método es privado por una razón, ya que es un detalle de implementación. Uno solo debe preocuparse por el comportamiento de la interfaz pública y no por cómo se implementa bajo las cubiertas.

Si deseo probar el comportamiento del método privado, al utilizar refactorizaciones comunes, puedo extraer su código en otra clase (tal vez con visibilidad a nivel de paquete, así que asegúrese de que no sea parte de una API pública). Entonces puedo probar su comportamiento de forma aislada.

El producto de la refactorización significa que el método privado ahora es una clase separada que se ha convertido en colaboradora de la clase original. Su comportamiento se habrá comprendido bien a través de sus propias pruebas unitarias.

Luego puedo simular su comportamiento cuando trato de probar la clase original para luego concentrarme en probar el comportamiento de la interfaz pública de esa clase en lugar de tener que probar una explosión combinatoria de la interfaz pública y el comportamiento de todos sus métodos privados .

Veo esto análogo a conducir un automóvil. Cuando manejo un automóvil, no manejo con el capó levantado, así puedo ver que el motor está funcionando. Confío en la interfaz que proporciona el automóvil, a saber, el cuentarrevoluciones y el velocímetro para saber que el motor está funcionando. Confío en el hecho de que el automóvil realmente se mueve cuando presiono el acelerador. Si quiero probar el motor, puedo hacer comprobaciones en forma aislada. :RE

Por supuesto, probar métodos privados directamente puede ser un último recurso si tiene una aplicación heredada, pero preferiría que el código heredado se refactorice para permitir mejores pruebas. Michael Feathers ha escrito un gran libro sobre este mismo tema. http://www.amazon.co.uk/Working-Effectively-Legacy-Robert-Martin/dp/0131177052

Los tipos privados, internos y miembros privados lo son por alguna razón y, a menudo, no quiere meterse con ellos directamente. Y si lo hace, es probable que se rompa más tarde, porque no hay garantía de que los tipos que crearon esas ensamblajes mantengan las implementaciones privadas / internas como tales.

Pero, a veces, cuando realizo algunos hacks / exploración de ensamblados comstackdos o de terceros, yo mismo terminé queriendo inicializar una clase privada o una clase con un constructor privado o interno. O, a veces, cuando trato con bibliotecas heredadas precomstackdas que no puedo cambiar, termino escribiendo algunas pruebas en contra de un método privado.

Así nació AccessPrivateWrapper – http://amazedsaint.blogspot.com/2010/05/accessprivatewrapper-c-40-dynamic.html – es una clase de contenedor rápido que facilitará el trabajo utilizando las características dinámicas y la reflexión de C # 4.0.

Puede crear tipos internos / privados como

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

También utilicé el método InternalsVisibleToAttribute. También vale la pena mencionar que, si se siente incómodo al hacer que sus métodos previamente privados sean internos para lograr esto, entonces tal vez no deban ser objeto de pruebas unitarias directas de todos modos.

Después de todo, está probando el comportamiento de su clase, en lugar de su implementación específica : puede cambiar la última sin cambiar la anterior y sus pruebas aún deben pasar.

Bueno, puedes probar el método privado de dos maneras

  1. puedes crear una instancia de la clase PrivateObject , la syntax es la siguiente

     PrivateObject obj= new PrivateObject(PrivateClass); //now with this obj you can call the private method of PrivateCalss. obj.PrivateMethod("Parameters"); 
  2. Puedes usar la reflexión

     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 tiene una buena característica integrada que hace que los miembros y métodos privados estén disponibles en el proyecto al crear un archivo llamado VSCodeGenAccessors

 [System.Diagnostics.DebuggerStepThrough()] [System.CodeDom.Compiler.GeneratedCodeAttribute("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 string ToString() { return this.Target.ToString(); } 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(); } } 

Con clases que derivan de BaseAccessor

como

 [System.Diagnostics.DebuggerStepThrough()] [System.CodeDom.Compiler.GeneratedCodeAttribute("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 string STATIC_STRING { get { string ret = ((string)(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; } 

Hay 2 tipos de métodos privados. Métodos privados estáticos y métodos privados no estáticos (métodos de instancia). Los siguientes 2 artículos explican cómo probar los métodos privados con ejemplos.

  1. Pruebas unitarias de métodos privados estáticos
  2. Pruebas unitarias Métodos privados no estáticos

Tiendo a no usar las directivas de comstackción porque complican las cosas rápidamente. Una manera de mitigarlo si realmente los necesita es colocarlos en una clase parcial y hacer que su comstackción ignore ese archivo .cs al crear la versión de producción.

En CodeProject, hay un artículo que analiza brevemente los pros y los contras de probar métodos privados. A continuación, proporciona un código de reflexión para acceder a métodos privados (similar al código que Marcus proporciona más arriba). El único problema que he encontrado con la muestra es que el código no tiene en cuenta los métodos sobrecargados.

Puede encontrar el artículo aquí:

http://www.codeproject.com/KB/cs/testnonpublicmembers.aspx

Demuestre que son internal , y luego use InternalsVisibleToAttribute para permitir que su unidad de prueba unitaria los vea.

A veces, puede ser bueno para probar declaraciones privadas. Fundamentalmente, un comstackdor solo tiene un método público: Comstackr (string outputFileName, params string [] sourceSFileNames). ¡Estoy seguro de que entiendes que sería difícil probar dicho método sin probar cada statement “oculta”!

Es por eso que hemos creado Visual T #: para hacer pruebas más fáciles. Es un lenguaje de progtwigción .NET gratuito (compatible con C # v2.0).

Hemos agregado el operador ‘.-‘. Simplemente se comporta como ‘.’ operador, excepto que también puede acceder a cualquier statement oculta de sus pruebas sin cambiar nada en su proyecto probado.

Echa un vistazo a nuestro sitio web: descárgalo gratis .

En primer lugar, no debería probar los métodos privados de su código. Debería probar la ‘interfaz pública’ o API, las cosas públicas de sus clases. La API son todos los métodos públicos que expone a los llamadores externos.

La razón es que una vez que comienzas a probar los métodos privados y las partes internas de tu clase, estás uniendo la implementación de tu clase (las cosas privadas) a tus pruebas. Esto significa que cuando decida cambiar los detalles de su implementación también deberá cambiar sus pruebas.

Por esta razón, debe evitar usar InternalsVisibleToAtrribute.

Aquí hay una gran charla de Ian Cooper que cubre este tema: Ian Cooper: TDD, ¿dónde salió todo mal?

MbUnit tiene un buen envoltorio para esto llamado Reflector.

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

También puedes establecer y obtener valores de propiedades

 dogReflector.GetProperty("Age"); 

En cuanto a la “prueba privada” estoy de acuerdo que … en el mundo perfecto. no tiene sentido hacer pruebas de unidades privadas. Pero en el mundo real, puede terminar queriendo escribir pruebas privadas en lugar de código de refactorización.

Me sorprende que nadie haya dicho esto todavía, pero una solución que he empleado es hacer un método estático dentro de la clase para probarse a sí mismo. Esto le da acceso a todo lo público y privado para probar.

Además, en un lenguaje de scripting (con habilidades OO, como Python, Ruby y PHP), puede hacer que el archivo se pruebe solo cuando se ejecuta. Una buena manera rápida de asegurarse de que los cambios no rompan nada. Obviamente, esta es una solución escalable para probar todas sus clases: simplemente ejecútelas todas. (También puede hacer esto en otros idiomas con un vacío principal que siempre ejecuta sus pruebas también).

Deseo crear aquí un ejemplo de código claro que pueda usar en cualquier clase en la que desee probar el método privado.

En su clase de casos de prueba solo incluya estos métodos y luego los emplee como se indica.

  /** * * @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));

Simplemente emita los parámetros en el orden en que aparecen en la función privada original

Para cualquiera que quiera ejecutar métodos privados sin todos los fess y mess. Esto funciona con cualquier marco de prueba de unidades que no use nada más que una buena reflexión antigua.

 public class ReflectionTools { // If the class is non-static public static Object InvokePrivate(Object objectUnderTest, string 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, string 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 string[] SplitInternal() { return Regex.Matches(Format, @"([^/\[\]]|\[[^]]*\])+") .Cast() .Select(m => m.Value) .Where(s => !string.IsNullOrEmpty(s)) .ToArray(); } 

Here’s the test:

 ///  ///A test for SplitInternal /// [TestMethod()] [DeploymentItem("Git XmlLib vs2008.dll")] public void SplitInternalTest() { string path = "pair[path/to/@Key={0}]/Items/Item[Name={1}]/Date"; object[] values = new object[] { 2, "Martin" }; XPathString xp = new XPathString(path, values); PrivateObject param0 = new PrivateObject(xp); XPathString_Accessor target = new XPathString_Accessor(param0); string[] expected = new string[] { "pair[path/to/@Key={0}]", "Items", "Item[Name={1}]", "Date" }; string[] 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 InternalsVisibleToAttribute) while building in debug-Mode:

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

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