¿Cómo se prueban unitariamente los métodos privados?

Resuelto Eric Labashosky asked hace 16 años • 32 respuestas

Estoy construyendo una biblioteca de clases que tendrá algunos métodos públicos y privados. Quiero poder realizar pruebas unitarias de 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?

Eric Labashosky avatar Oct 30 '08 22:10 Eric Labashosky
Aceptado

Si desea realizar una prueba unitaria de un método privado, es posible que algo esté mal. Las pruebas unitarias están (en términos generales) destinadas a probar la interfaz de una clase, es decir, sus métodos públicos (y protegidos). Por supuesto, puedes "piratear" una solución para esto (aunque solo sea haciendo públicos los métodos), pero también puedes considerar:

  1. Si realmente vale la pena probar el método que desea probar, puede que valga 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).
Jeroen Heijmans avatar Oct 30 '2008 15:10 Jeroen Heijmans

Si está utilizando .net, debe utilizar InternalsVisibleToAttribute .

TcKs avatar Oct 30 '2008 15:10 TcKs

Puede que no resulte ú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 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 => ​​Terminará con una nueva clase con el nombre foo_accessor. Esta clase se generará dinámicamente durante la compilación y proporcionará acceso público a todos los miembros.

Sin embargo, el mecanismo es a veces un poco intratable 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[] {/* ... */} );
Seven avatar Oct 29 '2010 13:10 Seven

No estoy de acuerdo con la filosofía de "solo debería interesarte probar la interfaz externa". Es un poco como decir que un taller de reparación de automóviles sólo debería hacer pruebas para ver si las ruedas giran. Sí, en última instancia, estoy interesado en el comportamiento externo, pero me gusta que mis pruebas internas, privadas, sean un poco más específicas y vayan al grano. Sí, si refactorizo, es posible que tenga que cambiar algunas de las pruebas, pero a menos que sea una refactorización masiva, solo tendré que cambiar algunas y el hecho de que las otras pruebas internas (sin cambios) sigan funcionando es un gran indicador de que la refactorización ha sido exitosa.

Puede intentar cubrir todos los casos internos usando solo la interfaz pública y, en teoría, es posible probar todos los métodos internos (o al menos todos los que importan) completamente usando la interfaz pública, pero es posible que tenga que terminar de cabeza para lograrlo. 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 para la que están diseñados pueden ser difíciles o imposibles de discernir. Habiendo señalado, 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 realizar grandes cambios en sus pruebas para cada refactorización, entonces tal vez esto no tenga sentido, pero en ese caso, tal vez debería repensar 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.

Darrell Plank avatar Oct 19 '2010 06:10 Darrell Plank

En los raros casos en los que he querido probar funciones privadas, normalmente las he modificado para que estén protegidas y he escrito una subclase con una función contenedora pública.

La clase:

...

protected void APrivateFunction()
{
    ...
}

...

Subclase para pruebas:

...

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

...
Jason Jackson avatar Oct 30 '2008 16:10 Jason Jackson