¿Cómo se prueba una función privada o una clase que se ha privado de los métodos, campos o las clases internas?

StackOverflow https://stackoverflow.com/questions/34571

  •  09-06-2019
  •  | 
  •  

Pregunta

¿Cómo puedo unidad de prueba (utilizando xUnit) una clase interna privada métodos, campos o de las clases anidadas?O una función que se realiza privado por tener vinculación interna (static en C/C++) o en privado (anónimo espacio de nombres?

Parece mala para cambiar el modificador de acceso para un método o función sólo para ser capaz de ejecutar una prueba.

¿Fue útil?

Solución

Actualización:

Unos 10 años más tarde, tal vez la mejor manera de poner a prueba un método privado, o cualquier miembro inaccesible, es a través de @Jailbreak a partir de la Colector de marco.

@Jailbreak Foo foo = new Foo();
// Direct, *type-safe* access to *all* foo's members
foo.privateMethod(x, y, z);
foo.privateField = value;

De esta manera el código sigue siendo el tipo de seguro y de fácil lectura.Sin comprometer el diseño, no sobreexposición de los métodos y los campos para el bien de las pruebas.

Si usted tiene un poco de un legado Java aplicación, y no está permitido cambiar la visibilidad de sus métodos, la mejor manera de poner a prueba los métodos privados es el uso de la reflexión.

Internamente estamos usando ayudantes para obtener/establecer private y private static variables así como invocar private y private static métodos.Las siguientes pautas le permiten hacer casi cualquier cosa relacionada con los métodos privados y campos.Por supuesto, usted no puede cambiar private static final las variables a través de la reflexión.

Method method = TargetClass.getDeclaredMethod(methodName, argClasses);
method.setAccessible(true);
return method.invoke(targetObject, argObjects);

Y para los campos:

Field field = TargetClass.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object, value);

Notas:
1. TargetClass.getDeclaredMethod(methodName, argClasses) te permite buscar en private métodos.Lo mismo se aplica para getDeclaredField.
2.El setAccessible(true) es necesario jugar con los soldados.

Otros consejos

La mejor manera de probar un método privado es a través de otro método público.Si esto no puede hacerse, entonces una de las siguientes condiciones es verdadera:

  1. El método privado es de código muerto
  2. Hay un diseño de olor a cerca de la clase que se está probando
  3. El método que usted está tratando de prueba no debe ser privado

Cuando me han privado de los métodos en una clase que son lo suficientemente complicado que siento la necesidad de poner a prueba los métodos privados directamente, que es un código de olor:mi clase es demasiado complicado.

Mi enfoque habitual para abordar estas cuestiones es la de desentrañar una nueva clase que contiene los bits interesantes.A menudo, este método y los campos en los que se interactúa, y tal vez otro método o dos puede ser extraído en una nueva clase.

La nueva clase expone estos métodos como "público", por lo que son accesibles para las pruebas unitarias.Las nuevas y las viejas clases son ahora más simple que el original de la clase, lo cual es genial para mí (tengo que mantener las cosas simples, o me pierdo!).

Tenga en cuenta que no estoy sugiriendo que la gente crea clases sin el uso de su cerebro!El punto aquí es el uso de las fuerzas de la unidad de prueba para ayudarle a encontrar buenas nuevas clases.

He utilizado la reflexión para hacer esto para Java en el pasado, y en mi opinión fue un gran error.

Estrictamente hablando, usted debe no escribir pruebas unitarias que probar directamente los métodos privados.Lo que debe ser la prueba es el contrato público de que la clase tiene con otros objetos;nunca se debe probar directamente un objeto de elementos internos.Si otro desarrollador quiere hacer un pequeño cambio interno a la clase, que no afecta a las clases contrato público, él/ella tiene que modificar su reflexión, basado en la prueba para asegurarse de que funciona.Si usted hace esto varias veces a lo largo de un proyecto, la unidad de pruebas, a continuación, dejar de ser una medida útil de código de la salud, y empezar a ser un obstáculo para el desarrollo, y una molestia para el equipo de desarrollo.

Lo que yo recomiendo hacer en cambio es el uso de una herramienta de cobertura de código, tales como Cobertura, para garantizar que la unidad de pruebas de escribir proporcionar decente de cobertura de código en los métodos privados.De esa manera, indirectamente prueba de lo que los métodos privados están haciendo, y mantener un alto nivel de agilidad.

A partir de este artículo: Las pruebas de los Métodos Privados con JUnit y SuiteRunner (Proyecto de ley Venners), básicamente tienes 4 opciones:

  • No prueba los métodos privados.
  • Dar a los métodos de acceso de paquete.
  • El uso de un entramado clase de prueba.
  • El uso de la reflexión.

Generalmente una unidad de prueba está pensada para el ejercicio de la interfaz pública de la clase o de la unidad.Por lo tanto, los métodos privados están detalle de implementación que no es de esperar de la prueba de forma explícita.

Sólo dos ejemplos de donde me gustaría probar un método privado:

  1. Rutinas de descifrado - Yo no queremos hacerlos visibles a cualquier persona a ver sólo para el bien de la prueba, cosa que cualquiera puede utilizar para descifrar.Pero son intrínsecos del código, complicado, y la necesidad de trabajar siempre (la excepción obvia es la reflexión que se puede utilizar para ver, incluso los métodos privados en la mayoría de los casos, cuando SecurityManager no está configurado para evitar esto).
  2. La creación de un SDK para la comunidad consumo.Aquí el público adquiere un totalmente diferente significado, ya que este es el código que todo el mundo puede ver (no sólo interna de mi solicitud).Pongo código en los métodos privados si no me quiere el SDK a los usuarios a ver - me no veo esto como el código de olor, simplemente cómo SDK de programación de obras.Pero de supuesto, todavía tengo que probar mi los métodos privados, y es donde la funcionalidad de mi SDK de realidad vidas.

Entiendo la idea de sólo prueba el "contrato".Pero no veo uno puede abogar por que en realidad no es prueba de código - su kilometraje puede variar.

Así que mi compromiso implica que complica la JUnits con la reflexión, en lugar de comprometer mi seguridad y SDK.

Los métodos privados son llamados por un método público, por lo que las entradas para sus métodos públicos debe también probar los métodos privados que son llamados por los métodos públicos.Cuando un método falla, entonces podría ser un fallo en el método privado.

Otro método que he utilizado es cambiar a un método privado para paquete de private o protected luego complementa con la @VisibleForTesting anotación de Google de la Guayaba de la biblioteca.

Esto le dirá a nadie que utilice este método para tomar la precaución y no acceder a él directamente, incluso en un paquete.También una clase de prueba no necesitan estar en el mismo paquete físicamente, pero en el mismo paquete en el prueba carpeta.

Por ejemplo, si un método a ser probada en src/main/java/mypackage/MyClass.java entonces tu llamada de prueba debe ser colocado en src/test/java/mypackage/MyClassTest.java.De esa manera, usted tiene acceso a la prueba de método en la clase de prueba.

Para probar el código de la herencia con gran y peculiar de las clases, es a menudo muy útil ser capaz de poner a prueba la privada (o pública) método que estoy escribiendo ahora mismo.

Yo uso el junitx.util.PrivateAccessor-el paquete de Java .Muchas líneas de código para acceder a métodos privados y campos privados.

import junitx.util.PrivateAccessor;

PrivateAccessor.setField(myObjectReference, "myCrucialButHardToReachPrivateField", myNewValue);
PrivateAccessor.invoke(myObjectReference, "privateMethodName", java.lang.Class[] parameterTypes, java.lang.Object[] args);

Después de haber probado Cem Catikkas' solución utilizando la reflexión para Java, yo tendría que decir que la suya era una solución más elegante que he descrito aquí.Sin embargo, si usted está buscando una alternativa para el uso de la reflexión, y tener acceso a la fuente que se está probando, esta será una opción.

No es posible mérito en las pruebas de los métodos privados de una clase, en particular con test-driven development, donde te gustaría diseñar pequeñas pruebas antes de escribir ningún código.

La creación de una prueba con el acceso a los miembros privados y los métodos de prueba de las zonas de código que son difíciles de atacar específicamente con acceso sólo a los métodos públicos.Si un método público tiene varios pasos involucrados, puede consistir de varios métodos privados, el cual puede ser probado de forma individual.

Ventajas:

  • Puede probar a una granularidad más fina

Desventajas:

  • El código de la prueba debe residir en el mismo archivo de código fuente, que puede ser más difícil de mantener
  • De igual manera, con .clase de archivos de salida, deben permanecer en el mismo paquete como declarado en el código fuente

Sin embargo, si la prueba continua requiere este método, puede ser una señal de que los métodos privados deben ser extraídos, que podrían ser probados en el tradicional vía pública.

Aquí está una enrevesada ejemplo de cómo funcionaría:

// Import statements and package declarations

public class ClassToTest
{
    private int decrement(int toDecrement) {
        toDecrement--;
        return toDecrement;
    }

    // Constructor and the rest of the class

    public static class StaticInnerTest extends TestCase
    {
        public StaticInnerTest(){
            super();
        }

        public void testDecrement(){
            int number = 10;
            ClassToTest toTest= new ClassToTest();
            int decremented = toTest.decrement(number);
            assertEquals(9, decremented);
        }

        public static void main(String[] args) {
            junit.textui.TestRunner.run(StaticInnerTest.class);
        }
    }
}

El interior de la clase podría ser compilado para ClassToTest$StaticInnerTest.

Vea también: Java Punta 106:Estática de las clases internas para la diversión y el beneficio

Como han dicho otros...no prueba los métodos privados directamente.Aquí están algunas ideas:

  1. Mantenga todos los métodos de pequeña y centrada (fácil de probar, fácil de encontrar lo que está mal)
  2. El uso de cobertura de código herramientas.Me gusta Cobertura (oh día feliz, parece que una nueva versión está!)

Ejecutar el código de la cobertura en la unidad de pruebas.Si usted ve que los métodos no son totalmente probado a añadir a las pruebas para obtener la cobertura.Objetivo para el 100% de cobertura de código, pero se dan cuenta de que probablemente no lo consigue.

Los métodos privados son consumidos por parte de los públicos.De lo contrario, ellos están muertos código.Es por eso que usted pruebe el método público, afirmando que los resultados esperados del método público y por lo tanto, los métodos privados que consume.

Las pruebas de los métodos privados deben ser probados por la depuración antes de ejecutar las pruebas unitarias sobre los métodos públicos.

También pueden ser depurado utilizando el test-driven development, la depuración de la unidad de pruebas hasta que todas sus afirmaciones se cumplen.

Yo personalmente creo que es mejor crear clases usando TDD;la creación de un método público de salario, a continuación, generación de pruebas de unidad con todos las afirmaciones definido de antemano, por lo que el resultado que se espera de el método se determina antes de que el código de la misma.De esta manera, usted no va por el camino equivocado de hacer la prueba de la unidad afirmaciones se ajuste a los resultados.Su clase es robusto y cumple con los requisitos cuando todos sus pruebas unitarias.

En el Framework Spring usted puede probar los métodos privados el uso de este método:

ReflectionTestUtils.invokeMethod()

Por ejemplo:

ReflectionTestUtils.invokeMethod(TestClazz, "createTest", "input data");

Si se utiliza la Primavera, ReflectionTestUtils proporciona algunas herramientas que ayudan a salir de aquí con el mínimo esfuerzo.Por ejemplo, para configurar un simulacro en un miembro privado, sin estar obligado a añadir un indeseable público setter:

ReflectionTestUtils.setField(theClass, "theUnsettableField", theMockObject);

Si usted está tratando de poner a prueba el código existente que está poco dispuesto o incapaz de cambiar, la reflexión es una buena opción.

Si la clase de diseño es aún flexible, y usted tiene un complicado método privado que te gustaría probar por separado, te sugiero que te tire de ella en una clase separada y prueba de esa clase por separado.Esto no tiene que cambiar la interfaz pública de la clase original;puede internamente crear una instancia de la clase auxiliar y llamar al método auxiliar.

Si desea probar las difíciles condiciones de error proveniente del método auxiliar, usted puede ir un paso más allá.Extracto de una interfaz de la clase auxiliar, agregar un público getter y setter para la clase original para inyectar la clase auxiliar (se utiliza a través de su interfaz), y, a continuación, inyectar una versión burlesca de la clase auxiliar en la clase original para probar cómo la clase original responde a las excepciones del ayudante.Este enfoque también es útil si desea probar la clase original sin, además de pruebas de la clase auxiliar.

Las pruebas de los métodos privados rompe la encapsulación de su clase, ya que cada vez que cambiar la implementación interna de romper el código de cliente (en este caso, las pruebas).

Así que no pongas a prueba los métodos privados.

La respuesta de JUnit.org página de preguntas frecuentes:

Pero si usted debe...

Si usted está utilizando el JDK 1.3 o posterior, puede utilizar la reflexión para subvertir el mecanismo de control de acceso con la ayuda de la PrivilegedAccessor.Para más detalles sobre cómo utilizarlo, lea este artículo.

Si usted está utilizando el JDK 1.6 o superior, y anotar sus pruebas con @Prueba, puede utilizar Dp4j para inyectar la reflexión en los métodos de prueba.Para los detalles sobre cómo utilizar, consulte esta secuencia de comandos de prueba.

P. S.Yo soy el principal contribuyente a Dp4j, pregunte me si usted necesita ayuda.:)

Si desea probar los métodos privados de un legado de la aplicación donde usted no puede cambiar el código, una opción para Java es jMockit, que te permitirá crear burla a un objeto, incluso cuando están privadas de la clase.

Yo no tienden a prueba los métodos privados.Ahí está la locura.Personalmente, creo que sólo debe probar sus interfaces que se exponen públicamente (y que incluye protegidas y los métodos internos).

Si usted está usando JUnit, echar un vistazo a junit-complementos.Tiene la capacidad de ignorar el modelo de seguridad de Java y privado de acceso a los métodos y atributos.

Un método privado es sólo para ser consultado dentro de la misma clase.Así que no hay manera de poner a prueba un "privado" método de una clase de destino de cualquier clase de prueba.Una manera de que usted puede realizar pruebas de unidad manualmente o puede cambiar su método de "privado", "protegidos".

Y, a continuación, un método protegido sólo puede ser accedido en el mismo paquete donde se define la clase.Así, las pruebas de un método protegido de una clase de destino significa que tenemos que definir la clase de prueba en el mismo paquete que la clase de destino.

Si todo lo anterior no se adapte a sus necesidades, el uso de la reflexión manera para acceder al método privado.

Yo sugiero que refactorizar el código un poco.Cuando tienes que empezar a pensar en el uso de la reflexión o de otro tipo de cosas, sólo para probar su código, algo va mal con su código.

Usted menciona los diferentes tipos de problemas.Vamos a empezar con los campos privados.En el caso de campos privados yo habría añadido un nuevo constructor y se inyecta campos en que.En lugar de esto:

public class ClassToTest {

    private final String first = "first";
    private final List<String> second = new ArrayList<>();
    ...
}

Me gustaría han utilizado este:

public class ClassToTest {

    private final String first;
    private final List<String> second;

    public ClassToTest() {
        this("first", new ArrayList<>());
    }

    public ClassToTest(final String first, final List<String> second) {
        this.first = first;
        this.second = second;
    }
    ...
}

Esto no será un problema, incluso con algún código heredado.Código antiguo se utiliza un constructor vacío, y si me preguntan, refactorizado el código tendrá un aspecto más limpio, y usted será capaz de inyectar valores necesarios en la prueba sin necesidad de reflexión.

Ahora acerca de los métodos privados.En mi experiencia personal cuando usted tiene a la punta de un método privado para la prueba, entonces el método no tiene nada que hacer en esa clase.Un patrón común, en ese caso, sería envuelva dentro de una interfaz, como Callable y luego de pasar en esa interfaz también en el constructor (con múltiples constructor truco):

public ClassToTest() {
    this(...);
}

public ClassToTest(final Callable<T> privateMethodLogic) {
    this.privateMethodLogic = privateMethodLogic;
}

La mayoría de todos los que he escrito parece que es un patrón de inyección de dependencias.En mi experiencia personal es muy útil durante la prueba, y creo que este tipo de código es más limpio y más fácil de mantener.Me gustaría decir lo mismo acerca de las clases anidadas.Si una clase anidada contiene pesada lógica sería mejor si se había movido como un paquete de clases privadas y se han inyectado en una clase de necesidad.

También hay varios otros patrones de diseño que he utilizado, mientras que la refactorización y mantener el código de la herencia, pero todo depende de los casos, de su código de prueba.El uso de la reflexión en su mayoría no es un problema, pero cuando tienes una aplicación empresarial que es muy probados y las pruebas se ejecutan antes de la implementación de todo lo que se pone muy lento (es sólo una molestia, y no me gusta ese tipo de cosas).

También hay setter de la inyección, pero yo no se recomienda el uso de la misma.Será mejor que me pegue con un constructor y de inicializar todo cuando es realmente necesario, dejando la posibilidad para la inyección de dependencias necesarias.

Como muchos anteriores han sugerido, una buena forma es poner a prueba a ellos a través de sus interfaces públicas.

Si usted hace esto, es una buena idea usar una herramienta de cobertura de código (como Emma) para ver si sus métodos privados son, de hecho, ser ejecutado desde sus pruebas.

Aquí está mi función genérica a prueba en los campos privados:

protected <F> F getPrivateField(String fieldName, Object obj)
    throws NoSuchFieldException, IllegalAccessException {
    Field field =
        obj.getClass().getDeclaredField(fieldName);

    field.setAccessible(true);
    return (F)field.get(obj);
}

Por favor vea a continuación un ejemplo;

La siguiente declaración de importación debe añadirse:

import org.powermock.reflect.Whitebox;

Ahora usted puede pasar directamente el objeto que tiene el método privado, el nombre del método a llamar, y los parámetros adicionales de la siguiente manera.

Whitebox.invokeMethod(obj, "privateMethod", "param1");

Hoy, me empujó a una biblioteca de Java para ayudar a probar los métodos privados y campos.Ha sido diseñado con Android en mente, pero realmente puede ser utilizado para cualquier proyecto de Java.

Si tienes un poco de código con los métodos privados o de los campos o de los constructores, se puede utilizar BoundBox.Hace exactamente lo que usted está buscando.Aquí abajo es un ejemplo de una prueba que tiene acceso a dos campos privados de Android de la actividad para la prueba:

@UiThreadTest
public void testCompute() {

    // Given
    boundBoxOfMainActivity = new BoundBoxOfMainActivity(getActivity());

    // When
    boundBoxOfMainActivity.boundBox_getButtonMain().performClick();

    // Then
    assertEquals("42", boundBoxOfMainActivity.boundBox_getTextViewMain().getText());
}

BoundBox hace que sea fácil para probar privado y protegido de los campos, métodos y constructores.Usted puede incluso tener acceso a cosas que están ocultas por la herencia.De hecho, BoundBox rompe la encapsulación.Se le dará acceso a todas las que a través de la reflexión, PERO todo lo que se comprueba en tiempo de compilación.

Es ideal para probar algún código heredado.Utilizar con precaución.;)

https://github.com/stephanenicolas/boundbox

En primer lugar, voy a lanzar esta pregunta:¿Por qué sus miembros privados necesitan pruebas aisladas?Son los que complejo, proporcionando tales comportamientos complicados como para requerir una prueba aparte de la superficie pública?Es la unidad de pruebas, no 'línea de código de pruebas.No se preocupe por las cosas pequeñas.

Si están tan grande, tan grande que estos miembros privados son cada "unidad" de gran complejidad-considerar la refactorización privada de los miembros de esta clase.

Si la refactorización es inadecuado o imposible, se puede utilizar el patrón de estrategia para reemplazar el acceso a las funciones miembro / miembro clases, cuando en virtud de la prueba de unidad?En la unidad de prueba, la estrategia de ofrecer mayor validación, pero en las versiones de lanzamiento sería sencillo paso.

Recientemente he tenido este problema y escribió una pequeña herramienta, llamada Ganzúa, que evita los problemas de forma explícita el uso de la reflexión Java API, dos ejemplos:

Llamar a los métodos, por ejemplo, private void method(String s) - por la reflexión Java

Method method = targetClass.getDeclaredMethod("method", String.class);
method.setAccessible(true);
return method.invoke(targetObject, "mystring");

Llamar a los métodos, por ejemplo, private void method(String s) - por Ganzúa

interface Accessible {
  void method(String s);
}

...
Accessible a = ObjectAccess.unlock(targetObject).features(Accessible.class);
a.method("mystring");

Campos de la configuración, por ejemplo, private BigInteger amount; - por la reflexión Java

Field field = targetClass.getDeclaredField("amount");
field.setAccessible(true);
field.set(object, BigInteger.valueOf(42));

Campos de la configuración, por ejemplo, private BigInteger amount; - por Ganzúa

interface Accessible {
  void setAmount(BigInteger amount);
}

...
Accessible a = ObjectAccess.unlock(targetObject).features(Accessible.class);
a.setAmount(BigInteger.valueOf(42));

Para Java me gustaría usar la reflexión, ya que no me gusta la idea de cambiar el acceso a un paquete en el método declarado justo por el bien de la prueba.Sin embargo, por lo general basta probar los métodos públicos que deben garantizar también los métodos privados están trabajando correctamente.

usted no puede utilizar la reflexión para obtener métodos privados desde fuera el dueño de la clase, el modificador private afecta a la reflexión también

Esto no es cierto.Usted seguramente puede, como se mencionó en Cem Catikkas la respuesta.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top