Estoy construyendo una biblioteca de clase que tendrá algunos métodos públicos y privados. Quiero poder realizar una prueba unitaria 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?
.net
unit-testing
tdd
private
Eric Labashosky
fuente
fuente
pre-historic
en términos de años de Internet, pero las pruebas unitarias de métodos privados ahora son fáciles y directas, ya que Visual Studio produce las clases de acceso necesarias cuando es necesario y llenar previamente la lógica de las pruebas con fragmentos muy cercanos a lo que uno puede desear para pruebas funcionales simples. Ver por ej. msdn.microsoft.com/en-us/library/ms184807%28VS.90%29.aspxRespuestas:
Si está usando .net, debe usar InternalsVisibleToAttribute .
fuente
#if DEBUG
elInternalsVisibleTo
atributo para que no se aplique al código de lanzamiento?#if RELEASE_TEST
alrededorInternalsVisibleTo
como Mike sugiere, y hacer una copia de la configuración de su versión de lanzamiento que defineRELEASE_TEST
. Puede probar su código de lanzamiento con optimizaciones, pero cuando realmente compila para el lanzamiento, sus pruebas serán omitidas.Si desea probar un método privado, algo puede estar mal. Las pruebas unitarias son (en términos generales) destinadas a probar la interfaz de una clase, es decir, sus métodos públicos (y protegidos). Por supuesto, puede "hackear" una solución a esto (incluso si solo hace públicos los métodos), pero también puede considerar:
fuente
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
Sin embargo, el mecanismo a veces es 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
fuente
No estoy de acuerdo con la filosofía de "solo deberías estar interesado en probar la interfaz externa". Es un poco como decir que un taller de reparación de automóviles solo debe realizar pruebas para ver si las ruedas giran. Sí, en última instancia, estoy interesado en el comportamiento externo, pero me gusta que mis propias pruebas privadas e internas sean un poco más específicas y precisas. Sí, si refactorizo, es posible que tenga que cambiar algunas de las pruebas, pero a menos que sea un refactor masivo, 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 utilizando solo la interfaz pública y, en teoría, es posible probar todos los métodos internos (o al menos todos los que importan) por completo mediante el uso de la interfaz pública, pero es posible que deba 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. Una vez 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 hacer 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.
fuente
FooService
tiene que hacer algoX
, lo único que debe importarle es que lo hagaX
cuando se le solicite. Cómo lo hace no debería importar. Si hay problemas en la clase no discernibles a través de la interfaz (poco probable), sigue siendo válidoFooService
. Si se trata de un problema que es visible a través de la interfaz, una prueba sobre los miembros públicos debe detectarlo. El punto principal debe ser que, siempre que la rueda gire correctamente, se puede usar como rueda.PrivMethod
, ¿una prueba enPubMethod
qué llamadasPrivMethod
debería exponerlo? ¿Qué sucede cuando cambias tuSimpleSmtpService
a aGmailService
? De repente, sus pruebas privadas apuntan a un código que ya no existe o que tal vez funciona de manera diferente y fallaría, a pesar de que la aplicación puede funcionar perfectamente como se diseñó. Si hay un procesamiento complejo que se aplicaría a ambos remitentes de correo electrónico, ¿tal vez debería estar en unoEmailProcessor
que ambos puedan usar y probar por separado?En los raros casos en los que he querido probar funciones privadas, generalmente las modifiqué para protegerlas y escribí una subclase con una función de envoltura pública.
La clase:
Subclase para pruebas:
fuente
Creo que una pregunta más fundamental que debería hacerse es: ¿por qué estás tratando de 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, no por cómo se implementa bajo las cubiertas.
Si quiero probar el comportamiento del método privado, mediante el uso de 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á entendido bien a través de sus propias pruebas unitarias.
Entonces puedo burlarme de su comportamiento cuando intento probar la clase original para poder 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 conduzco un automóvil, no conduzco con el capó hacia arriba, 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 si el motor funciona. Confío en el hecho de que el automóvil realmente se mueve cuando presiono el acelerador. Si quiero probar el motor, puedo hacer controles de 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
fuente
Los tipos privados, los internos y los miembros privados lo son por alguna razón, y a menudo no desea meterse con ellos directamente. Y si lo hace, lo más probable es que se rompa más tarde, porque no hay garantía de que los chicos que crearon esos ensamblajes mantendrán las implementaciones privadas / internas como tales.
Pero, a veces, al hacer algunos hacks / exploración de ensamblados compilados o de terceros, terminé queriendo inicializar una clase privada o una clase con un constructor privado o interno. O, a veces, cuando trato con bibliotecas heredadas precompiladas que no puedo cambiar, termino escribiendo algunas pruebas contra un método privado.
Así nació AccessPrivateWrapper - http://amazedsaint.blogspot.com/2010/05/accessprivatewrapper-c-40-dynamic.html - es una clase de envoltura rápida que facilitará el trabajo utilizando las características dinámicas y la reflexión de C # 4.0.
Puede crear tipos internos / privados como
fuente
Bueno, puedes probar el método privado de dos maneras
puede crear una instancia de
PrivateObject
clase, la sintaxis es la siguientePuedes usar la reflexión.
fuente
PrivateClass
Primero debe declarar una instancia de y usar eso. stackoverflow.com/questions/9122708/…También he usado el método InternalsVisibleToAttribute. Vale la pena mencionar también que, si se siente incómodo al hacer que sus métodos privados anteriores sean internos para lograr esto, entonces tal vez no deberían 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 el último sin cambiar el primero y sus pruebas aún deben pasar.
fuente
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 métodos privados con ejemplos.
fuente
MS Test tiene una buena característica incorporada que hace que los miembros y métodos privados estén disponibles en el proyecto mediante la creación de un archivo llamado VSCodeGenAccessors
Con clases que derivan de BaseAccessor
como
fuente
En CodeProject, hay un artículo que trata brevemente los pros y los contras de probar métodos privados. Luego proporciona un código de reflexión para acceder a métodos privados (similar al código que Marcus proporciona anteriormente). 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
fuente
Declarelos
internal
y luego use elInternalsVisibleToAttribute
para permitir que su unidad de prueba los vea.fuente
Tiendo a no usar las directivas del compilador porque desordenan las cosas rápidamente. Una forma de mitigarlo si realmente los necesita es colocarlos en una clase parcial y hacer que su compilación ignore ese archivo .cs al hacer la versión de producción.
fuente
En primer lugar, no debería probar los métodos privados de su código. Deberías probar la 'interfaz pública' o API, las cosas públicas de tus clases. La API son todos los métodos públicos que expone a las personas que llaman desde el exterior.
La razón es que una vez que comienza a probar los métodos privados y los elementos internos de su clase, está acoplando la implementación de su clase (las cosas privadas) a sus pruebas. Esto significa que cuando decida cambiar los detalles de su implementación, también tendrá que 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?
fuente
A veces, puede ser bueno probar las declaraciones privadas. Básicamente, un compilador solo tiene un método público: Compilar (string outputFileName, params string [] sourceSFileNames). ¡Estoy seguro de que comprende que sería difícil probar dicho método sin probar cada declaración "oculta"!
Es por eso que hemos creado Visual T #: para hacer pruebas más fáciles. Es un lenguaje de programació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 declaración oculta de sus pruebas sin cambiar nada en su proyecto probado.
Echar un vistazo a nuestro sitio web: descarga es gratis .
fuente
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 secuencias de comandos (con capacidades OO, como Python, Ruby y PHP), puede hacer que el archivo se pruebe en sí mismo cuando se ejecute. Buena forma rápida de asegurarse de que sus cambios no rompan nada. Obviamente, esto es una solución escalable para probar todas sus clases: simplemente ejecútelas todas. (también puede hacerlo en otros idiomas con un vacío principal que siempre ejecuta sus pruebas también).
fuente
Quiero crear un ejemplo de código claro aquí que puede usar en cualquier clase en la que desee probar el método privado.
En su clase de caso de prueba simplemente incluya estos métodos y luego utilícelos como se indica.
$ this -> _ callMethod ('_ someFunctionName', array (param1, param2, param3));
Simplemente emita los parámetros en el orden en que aparecen en la función privada original
fuente
Para cualquiera que quiera ejecutar métodos privados sin todos los fess y mess. Esto funciona con cualquier marco de prueba de unidad utilizando nada más que el viejo Reflection.
Luego, en sus pruebas reales, puede hacer algo como esto:
fuente
MbUnit tiene un buen contenedor para esto llamado Reflector.
También puede establecer y obtener valores de propiedades
En cuanto a la "prueba privada", estoy de acuerdo en que ... en el mundo perfecto. no tiene sentido hacer pruebas de unidades privadas. Pero en el mundo real, es posible que desee escribir pruebas privadas en lugar de refactorizar el código.
fuente
Reflector
ha sido reemplazado por el más poderosoMirror
en Gallio / MbUnit v3.2. ( gallio.org/wiki/doku.php?id=mbunit:mirror )Aquí hay un buen artículo sobre pruebas unitarias de métodos privados. Pero no estoy seguro de qué es mejor, hacer que su aplicación esté diseñada especialmente para pruebas (es como crear pruebas solo para pruebas) o usar reflexion para pruebas. Estoy bastante seguro de que la mayoría de nosotros elegiremos la segunda forma.
fuente
En mi opinión, solo debes probar la API pública de tu clase.
Hacer público un método, para probarlo unitariamente, rompe la encapsulación exponiendo los detalles de implementación.
Una buena API pública resuelve un objetivo inmediato del código del cliente y lo resuelve por completo.
fuente
Yo uso la clase PrivateObject . Pero como se mencionó anteriormente, es mejor evitar probar métodos privados.
fuente
"CC" es el compilador de línea de comandos en el sistema que uso.
-Dfoo=bar
hace el equivalente de#define foo bar
. Por lo tanto, esta opción de compilación efectivamente cambia todas las cosas privadas a públicas.fuente
Aquí hay un ejemplo, primero la firma del método:
Aquí está la prueba:
fuente
Una forma de hacerlo es tener su método
protected
y escribir un accesorio de prueba que herede su clase para ser probada. De esta manera, no está cambiando su métodopublic
, pero habilita la prueba.fuente
1) Si tiene un código heredado, entonces la única forma de probar métodos privados es mediante reflexión.
2) Si se trata de un código nuevo, tiene las siguientes opciones:
Prefiero el método de anotación, el más simple y el menos complicado. El único problema es que hemos aumentado la visibilidad, lo que creo que no es una gran preocupación. Siempre deberíamos estar codificando para la interfaz, por lo que si tenemos una interfaz MyService y una implementación MyServiceImpl, entonces podemos tener las clases de prueba correspondientes que son MyServiceTest (métodos de interfaz de prueba) y MyServiceImplTest (métodos privados de prueba). De todos modos, todos los clientes deberían usar la interfaz, de modo que, aunque se haya aumentado la visibilidad del método privado, no debería importar realmente.
fuente
También puede declararlo como público o interno (con InternalsVisibleToAttribute) mientras construye en modo de depuración:
Hincha el código, pero estará
private
en una versión de lanzamiento.fuente
Puede generar el método de prueba para el método privado desde Visual Studio 2008. Cuando crea una prueba unitaria para un método privado, se agrega una carpeta de Referencias de prueba a su proyecto de prueba y se agrega un descriptor de acceso a esa carpeta. El descriptor de acceso también se menciona en la lógica del método de prueba de la unidad. Este descriptor de acceso permite que su unidad de prueba llame a métodos privados en el código que está probando. Para más detalles echa un vistazo a
http://msdn.microsoft.com/en-us/library/bb385974.aspx
fuente
También tenga en cuenta que InternalsVisibleToAtrribute tiene el requisito de que su ensamblado tenga un nombre seguro, lo que crea su propio conjunto de problemas si está trabajando en una solución que no había tenido ese requisito antes. Yo uso el descriptor de acceso para probar métodos privados. Vea esta pregunta que para un ejemplo de esto.
fuente
InternalsVisibleToAttribute
qué no requieren que sus montajes ser un nombre seguro. Actualmente lo uso en un proyecto donde ese no es el caso.