¿Cómo pruebo la unidad (usando xUnit) una clase que tiene métodos privados internos, campos o clases anidadas? ¿O una función que se hace privada al tener un enlace interno ( static
en C / C ++) o está en un espacio de nombres privado ( anónimo )?
Parece malo cambiar el modificador de acceso para un método o función solo para poder ejecutar una prueba.
java
unit-testing
tdd
Raedwald
fuente
fuente
Respuestas:
Si tienes algo de un legado aplicación Java y no puede cambiar la visibilidad de sus métodos, la mejor manera de probar métodos privados es usar la reflexión .
Internamente estamos usando ayudantes para obtener / establecer
private
yprivate static
variables, así como invocarprivate
yprivate static
métodos. Los siguientes patrones le permitirán hacer prácticamente cualquier cosa relacionada con los métodos y campos privados. Por supuesto, no puede cambiar lasprivate static final
variables a través de la reflexión.Y para campos:
fuente
La mejor manera de probar un método privado es a través de otro método público. Si esto no se puede hacer, entonces una de las siguientes condiciones es verdadera:
fuente
Cuando tengo métodos privados en una clase que son lo suficientemente complicados como para sentir la necesidad de probarlos directamente, es un olor a código: mi clase es demasiado complicada.
Mi enfoque habitual para abordar estos problemas es descifrar una nueva clase que contenga los bits interesantes. A menudo, este método y los campos con los que interactúa, y quizás otro método o dos se pueden extraer en una nueva clase.
La nueva clase expone estos métodos como 'públicos', por lo que son accesibles para pruebas unitarias. Las clases nuevas y antiguas ahora son más simples que la clase original, lo cual es genial para mí (¡necesito mantener las cosas simples o me pierdo!).
¡Tenga en cuenta que no estoy sugiriendo que las personas creen clases sin usar su cerebro! El punto aquí es utilizar las fuerzas de las pruebas unitarias para ayudarlo a encontrar buenas clases nuevas.
fuente
He utilizado la reflexión para hacer esto para Java en el pasado, y en mi opinión fue un gran error.
Estrictamente hablando, no debe escribir pruebas unitarias que prueben directamente métodos privados. Lo que deberias probar es el contrato público que la clase tiene con otros objetos; nunca debes probar directamente las partes internas de un objeto. Si otro desarrollador quiere hacer un pequeño cambio interno en la clase, lo que no afecta el contrato público de las clases, entonces él / ella tiene que modificar su prueba basada en la reflexión para asegurarse de que funcione. Si hace esto repetidamente a lo largo de un proyecto, las pruebas unitarias dejan de ser una medida útil del estado del código y comienzan a convertirse en un obstáculo para el desarrollo y una molestia para el equipo de desarrollo.
Lo que recomiendo hacer en su lugar es utilizar una herramienta de cobertura de código como Cobertura, para garantizar que las pruebas unitarias que escriba proporcionen una cobertura decente del código en métodos privados. De esa manera, usted prueba indirectamente lo que están haciendo los métodos privados y mantiene un mayor nivel de agilidad.
fuente
De este artículo: Prueba de métodos privados con JUnit y SuiteRunner (Bill Venners), básicamente tiene 4 opciones:
fuente
Generalmente, una prueba de unidad está destinada a ejercer la interfaz pública de una clase o unidad. Por lo tanto, los métodos privados son detalles de implementación que no esperaría probar explícitamente.
fuente
Solo dos ejemplos de dónde me gustaría probar un método privado:
SecurityManager
no está configurado para evitar esto).Entiendo la idea de probar solo el "contrato". Pero no veo que uno pueda abogar por no probar el código; su millaje puede variar.
Entonces, mi compensación implica complicar los JUnits con la reflexión, en lugar de comprometer mi seguridad y SDK.
fuente
private
no es la única alternativa. Ningún modificador de acceso espackage private
y significa que puede realizar una prueba unitaria mientras su prueba unitaria viva en el mismo paquete.Los métodos privados son llamados por un método público, por lo que las entradas a sus métodos públicos también deben probar los métodos privados que son llamados por esos métodos públicos. Cuando falla un método público, eso podría ser un error en el método privado.
fuente
Otro enfoque que he usado es cambiar un método privado para empaquetar privado o protegido y luego complementarlo con la anotación @VisibleForTesting de la biblioteca Google Guava.
Esto le indicará a cualquiera que use este método que tenga cuidado y no acceda directamente, incluso en un paquete. Además, una clase de prueba no necesita estar físicamente en el mismo paquete , sino en el mismo paquete debajo de la carpeta de prueba .
Por ejemplo, si hay un método para probar,
src/main/java/mypackage/MyClass.java
entonces su llamada de prueba debe realizarsesrc/test/java/mypackage/MyClassTest.java
. De esa manera, tienes acceso al método de prueba en tu clase de prueba.fuente
Para probar el código heredado con clases grandes y extravagantes, a menudo es muy útil poder probar el método privado (o público) que estoy escribiendo en este momento .
Yo uso el paquete junitx.util.PrivateAccessor para Java. Un montón de frases útiles para acceder a métodos privados y campos privados.
fuente
Class
como su primer parámetro en estos métodos, esté accediendo solo astatic
miembros.En Spring Framework , puede probar métodos privados utilizando este método:
Por ejemplo:
fuente
Después de haber probado la solución de Cem Catikkas usando la reflexión para Java, debo decir que fue una solución más elegante que la que he descrito aquí. Sin embargo, si está buscando una alternativa al uso de la reflexión y tiene acceso a la fuente que está probando, esta seguirá siendo una opción.
Existe la posibilidad de probar métodos privados de una clase, particularmente con el desarrollo basado en pruebas , en el que le gustaría diseñar pequeñas pruebas antes de escribir cualquier código.
Crear una prueba con acceso a miembros y métodos privados puede probar áreas de código que son difíciles de enfocar específicamente con acceso solo a métodos públicos. Si un método público tiene varios pasos involucrados, puede consistir en varios métodos privados, que luego pueden probarse individualmente.
Ventajas:
Desventajas
Sin embargo, si las pruebas continuas requieren este método, puede ser una señal de que deben extraerse los métodos privados, que podrían probarse de la manera tradicional y pública.
Aquí hay un ejemplo complicado de cómo funcionaría esto:
La clase interna se compilaría a
ClassToTest$StaticInnerTest
.Ver también: Java Tip 106: Clases internas estáticas para diversión y ganancias
fuente
Como otros han dicho ... no pruebes métodos privados directamente. Aquí hay algunos pensamientos:
Ejecute la cobertura del código en las pruebas unitarias. Si ve que los métodos no se prueban completamente, agréguelos a las pruebas para aumentar la cobertura. Apunte a una cobertura de código del 100%, pero tenga en cuenta que probablemente no la obtendrá.
fuente
Los métodos privados son consumidos por los públicos. De lo contrario, son código muerto. Es por eso que prueba el método público, afirmando los resultados esperados del método público y, por lo tanto, los métodos privados que consume.
La prueba de los métodos privados debe probarse mediante depuración antes de ejecutar las pruebas unitarias en los métodos públicos.
También se pueden depurar mediante el desarrollo basado en pruebas, depurando las pruebas unitarias hasta que se cumplan todas sus afirmaciones.
Personalmente, creo que es mejor crear clases con TDD; crear los resguardos de métodos públicos, luego generar pruebas unitarias con todas las aserciones definidas de antemano, de modo que el resultado esperado del método se determine antes de codificarlo. De esta manera, no va por el camino equivocado de hacer que las afirmaciones de la prueba unitaria se ajusten a los resultados. Su clase es robusta y cumple con los requisitos cuando pasan todas las pruebas de su unidad.
fuente
Si usa Spring, ReflectionTestUtils proporciona algunas herramientas útiles que ayudan aquí con un mínimo esfuerzo. Por ejemplo, para configurar un simulacro en un miembro privado sin verse obligado a agregar un setter público indeseable:
fuente
Si intenta probar el código existente que es reacio o no puede cambiar, la reflexión es una buena opción.
Si el diseño de la clase sigue siendo flexible, y tiene un método privado complicado que le gustaría probar por separado, le sugiero que lo saque a una clase separada y pruebe esa clase por separado. Esto no tiene que cambiar la interfaz pública de la clase original; internamente puede crear una instancia de la clase auxiliar y llamar al método auxiliar.
Si desea probar condiciones de error difíciles provenientes del método auxiliar, puede ir un paso más allá. Extraiga una interfaz de la clase auxiliar, agregue un captador y definidor público a la clase original para inyectar la clase auxiliar (utilizada a través de su interfaz), y luego inyecte una versión simulada de la clase auxiliar en la clase original para probar cómo funciona la clase original responde a excepciones del ayudante. Este enfoque también es útil si desea probar la clase original sin probar también la clase auxiliar.
fuente
Probar métodos privados rompe la encapsulación de su clase porque cada vez que cambia la implementación interna se rompe el código del cliente (en este caso, las pruebas).
Así que no pruebes métodos privados.
fuente
La respuesta de la página de preguntas frecuentes de JUnit.org :
PS Soy el principal contribuyente a Dp4j , pregunto yo si necesita ayuda. :)
fuente
Si desea probar métodos privados de una aplicación heredada donde no puede cambiar el código, una opción para Java es jMockit , que le permitirá crear simulacros para un objeto incluso cuando son privados para la clase.
fuente
Deencapsulation.invoke(instance, "privateMethod", param1, param2);
Tiendo a no probar métodos privados. Ahí yace la locura. Personalmente, creo que solo debe probar sus interfaces expuestas públicamente (y eso incluye métodos protegidos e internos).
fuente
Si está usando JUnit, eche un vistazo a junit-addons . Tiene la capacidad de ignorar el modelo de seguridad de Java y acceder a métodos y atributos privados.
fuente
Te sugiero que refactorices un poco tu código. Cuando tiene que comenzar a pensar en usar la reflexión u otro tipo de cosas, solo para probar su código, algo va mal con su código.
Mencionaste diferentes tipos de problemas. Comencemos con los campos privados. En el caso de campos privados, habría agregado un nuevo constructor e inyectado campos en eso. En lugar de esto:
Hubiera usado esto:
Esto no será un problema incluso con algún código heredado. El código anterior usará un constructor vacío, y si me preguntas, el código refactorizado se verá más limpio y podrás inyectar los valores necesarios en la prueba sin reflexión.
Ahora sobre métodos privados. En mi experiencia personal, cuando tienes que colocar un método privado para probar, ese método no tiene nada que ver en esa clase. Un patrón común, en ese caso, sería envolver dentro de una interfaz, como
Callable
y luego pasar esa interfaz también en el constructor (con ese truco de constructor múltiple):Casi todo lo que escribí parece que es un patrón de inyección de dependencia. En mi experiencia personal, es realmente útil durante las pruebas, y creo que este tipo de código es más limpio y será más fácil de mantener. Yo diría lo mismo sobre las clases anidadas. Si una clase anidada contiene una lógica pesada, sería mejor si la hubiera movido como una clase privada de paquete y la hubiera inyectado en una clase que la necesita.
También hay varios otros patrones de diseño que he usado al refactorizar y mantener el código heredado, pero todo depende de los casos de su código para probar. El uso de la reflexión en su mayoría no es un problema, pero cuando tienes una aplicación empresarial que está muy probada y las pruebas se ejecutan antes de cada implementación, todo se vuelve realmente lento (es molesto y no me gusta ese tipo de cosas).
También hay una inyección de setter, pero no recomendaría usarla. Mejor me quedo con un constructor e inicializo todo cuando sea realmente necesario, dejando la posibilidad de inyectar las dependencias necesarias.
fuente
ClassToTest(Callable)
inyección. Eso lo haceClassToTest
más complicado. Mantenlo simple. Además, eso requiere que alguien más digaClassToTest
algo de lo queClassToTest
debería ser el jefe. Hay un lugar para inyectar lógica, pero este no lo es. Acabas de hacer que la clase sea más difícil de mantener, no más fácil.X
no aumente laX
complejidad hasta el punto en que pueda causar más problemas ... y, por lo tanto, requiera pruebas adicionales ... que si lo ha implementado de una manera que puede causar más problemas ... . (esto no es un bucle infinito; cada iteración es probablemente más pequeña que la anterior, pero sigue siendo molesta)ClassToTest
más difícil de mantener? En realidad, hace que su aplicación sea más fácil de mantener, ¿qué sugiere hacer una nueva clase para cada valor diferente que necesitaráfirst
y las 'segundas' variables?class A{ A(){} f(){} }
es más simple queclass A { Logic f; A(logic_f) } class B { g() { new A( logic_f) } }
. Si eso no fuera cierto, y si fuera cierto que el suministro de la lógica de una clase como argumentos de constructor era más fácil de mantener, entonces pasaríamos toda la lógica como argumentos de constructor. Simplemente no veo cómo puede reclamarclass B{ g() { new A( you_dont_know_your_own_logic_but_I_do ) } }
alguna vez hace que A sea más fácil de mantener. Hay casos en los que inyectar así tiene sentido, pero no veo su ejemplo como "más fácil de mantener"Solo se puede acceder a un método privado dentro de la misma clase. Por lo tanto, no hay forma de probar un método "privado" de una clase objetivo desde ninguna clase de prueba. Una salida es que puede realizar pruebas unitarias manualmente o puede cambiar su método de "privado" a "protegido".
Y luego solo se puede acceder a un método protegido dentro del mismo paquete donde se define la clase. Por lo tanto, probar un método protegido de una clase objetivo significa que necesitamos definir su clase de prueba en el mismo paquete que la clase objetivo.
Si todo lo anterior no cumple con sus requisitos, use la forma de reflexión para acceder al método privado.
fuente
Como muchos han sugerido anteriormente, una buena manera es probarlos a través de sus interfaces públicas.
Si hace esto, es una buena idea usar una herramienta de cobertura de código (como Emma) para ver si sus métodos privados se están ejecutando de sus pruebas.
fuente
Aquí está mi función genérica para probar campos privados:
fuente
Consulte a continuación para ver un ejemplo;
Se debe agregar la siguiente declaración de importación:
Ahora puede pasar directamente el objeto que tiene el método privado, el nombre del método que se va a llamar y los parámetros adicionales como se muestra a continuación.
fuente
Hoy, introduje una biblioteca Java para ayudar a probar métodos y campos privados. Ha sido diseñado con Android en mente, pero realmente se puede usar para cualquier proyecto Java.
Si tiene algún código con métodos privados o campos o constructores, puede usar BoundBox . Hace exactamente lo que estás buscando. A continuación se muestra un ejemplo de una prueba que accede a dos campos privados de una actividad de Android para probarla:
BoundBox facilita la prueba de campos, métodos y constructores privados / protegidos. Incluso puedes acceder a cosas que están ocultas por herencia. De hecho, BoundBox rompe la encapsulación. Te dará acceso a todo eso a través de la reflexión, PERO todo se verifica en tiempo de compilación.
Es ideal para probar algunos códigos heredados. Úselo con cuidado. ;)
https://github.com/stephanenicolas/boundbox
fuente
Primero, haré esta pregunta: ¿Por qué sus miembros privados necesitan pruebas aisladas? ¿Son tan complejos y proporcionan comportamientos tan complicados que requieren pruebas aparte de la superficie pública? Es una prueba unitaria, no una prueba de "línea de código". No te preocupes por las cosas pequeñas.
Si son tan grandes, lo suficientemente grandes como para que estos miembros privados sean cada uno una 'unidad' de gran complejidad, considere refactorizar a tales miembros privados fuera de esta clase.
Si la refactorización es inapropiada o inviable, ¿puede usar el patrón de estrategia para reemplazar el acceso a estas funciones / clases de miembros privados cuando se realiza una prueba unitaria? Según la prueba unitaria, la estrategia proporcionaría una validación adicional, pero en las versiones de lanzamiento sería un paso simple.
fuente
Recientemente tuve este problema y escribí una pequeña herramienta, llamada Picklock , que evita los problemas de usar explícitamente la API de reflexión de Java, dos ejemplos:
Métodos de llamada, por ejemplo
private void method(String s)
, por reflexión de JavaMétodos de llamada, por ejemplo
private void method(String s)
, por PicklockConfiguración de campos, p
private BigInteger amount;
. Ej ., Por reflexión JavaConfiguración de campos, por ejemplo
private BigInteger amount;
, por Picklockfuente
PowerMockito está hecho para esto. Usar dependencia maven
Entonces puedes hacer
fuente