¿Cómo pruebo una función privada o una clase que tiene métodos privados, campos o clases internas?

¿Cómo pruebo unitariamente (usando xUnit) una clase que tiene métodos privados internos, campos o clases anidadas? O una función que se hace privada al tener vinculación interna (static en C / C ++) o en un privado (anónimo) espacio de nombres?

Parece malo cambiar el modificador de acceso de un método o función solo para poder ejecutar una prueba.

preguntado el 29 de agosto de 08 a las 14:08

La mejor manera de probar un método privado es no probarlo directamente:

30 Respuestas

Actualizar:

Unos 10 años después, quizás la mejor manera de probar un método privado, o cualquier miembro inaccesible, es a través de @Jailbreak from the colector marco de referencia.

@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, su código permanece legible y seguro para escribir. Sin compromisos de diseño, sin sobreexposición de métodos y campos por el bien de las pruebas.

Si tienes algo de legado Java aplicación, y no se le permite cambiar la visibilidad de sus métodos, la mejor manera de probar métodos privados es usar reflexión.

Internamente estamos usando ayudantes para obtener / configurar private y private static variables así como invocar private y private static métodos. Los siguientes patrones le permitirán hacer prácticamente cualquier cosa relacionada con los métodos y campos privados. Por supuesto que no puedes cambiar private static final variables a través de la reflexión.

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

Y para campos:

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

Notas
1. TargetClass.getDeclaredMethod(methodName, argClasses) te permite mirar dentro private métodos. Lo mismo se aplica para getDeclaredField.
2. los setAccessible(true) Se requiere jugar con los soldados rasos.

respondido 15 mar '19, 02:03

Es útil si quizás no conoces la API, pero si tienes que probar métodos privados de esta manera, hay algo en tu diseño. Como dice otro cartel, las pruebas unitarias deberían probar el contrato de la clase: si el contrato es demasiado amplio y ejemplifica demasiado del sistema, entonces se debe abordar el diseño. - andygavin

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

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

Respondido 29 ago 08, 17:08

Además, nunca nos acercamos al 100% de cobertura de código de todos modos, así que, ¿por qué no concentrar su tiempo en realizar pruebas de calidad en los métodos que los clientes realmente utilizarán directamente? - grinch

@grinch Spot en. Lo único que obtendría al probar métodos privados es depurar información, y para eso están los depuradores. Si sus pruebas del contrato de la clase tienen una cobertura completa, entonces tiene toda la información que necesita. Los métodos privados son una implementación detalle. Si los prueba, tendrá que cambiar sus pruebas cada vez que cambie su implementación, incluso si el contrato no lo hace. En el ciclo de vida completo del software, es probable que esto cueste mucho más que el beneficio que brinda. - erik madsen

Cuando tengo métodos privados en una clase que son lo suficientemente complicados como para sentir la necesidad de probar los métodos privados directamente, eso es un olor a código: mi clase es demasiado complicada.

Mi enfoque habitual para abordar estos problemas es buscar una nueva clase que contenga las partes 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 son ahora más sencillas 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 usar las fuerzas de las pruebas unitarias para ayudarlo a encontrar buenas clases nuevas.

contestado el 19 de mayo de 16 a las 10:05

La pregunta era cómo probar los métodos privados. Dices que harías una nueva clase para eso (y agregarías mucha más complejidad) y luego sugieres no crear una nueva clase. Entonces, ¿cómo probar los métodos privados? - Dainius

@Dainius: no sugiero crear una nueva clase únicamente para que pueda probar ese método. Sugiero que escribir pruebas puede ayudarte a mejorar tu diseño: los buenos diseños son fáciles de probar. - jay bazuzi

¿Pero está de acuerdo en que una buena OOD expondría (haría públicos) solo los métodos que son necesarios para que esa clase / objeto funcione correctamente? todos los demás deben ser privados / protegidos. Por lo tanto, en algunos métodos privados habrá algo de lógica y, en mi opinión, las pruebas de estos métodos solo mejorarán la calidad del software. Por supuesto, estoy de acuerdo en que si algún fragmento de código es demasiado complejo, debería dividirse en métodos / clases separados. - Dainius

@Danius: Al agregar una nueva clase, en la mayoría de los casos se reduce la complejidad. Solo mídelo. La testabilidad en algunos casos lucha contra OOD. El enfoque moderno es la probabilidad a favor. - Alex Wien

Es exactamente cómo utilizar los comentarios de sus pruebas para impulsar y mejorar su diseño. Escuche sus pruebas. si son difíciles de escribir, ¡eso te está diciendo algo importante sobre tu código! - pnschofield

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

Estrictamente hablando, deberías no estar escribiendo pruebas unitarias que prueben directamente métodos privados. Lo que tu debemos ser testing es el contrato público que la clase tiene con otros objetos; nunca debe probar directamente las partes internas de un objeto. Si otro desarrollador quiere hacer un pequeño cambio interno en la clase, que no afecta el contrato público de las clases, entonces 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 de la salud 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 usar una herramienta de cobertura de código como Cobertura, para garantizar que las pruebas unitarias que escribe brinden una cobertura decente del código en métodos privados. De esa manera, prueba indirectamente lo que están haciendo los métodos privados y mantiene un mayor nivel de agilidad.

Respondido el 14 de diciembre de 17 a las 12:12

+1 a esto. En mi opinión, es la mejor respuesta a la pregunta. Al probar métodos privados, está probando la implementación. Esto frustra el propósito de las pruebas unitarias, que es probar las entradas / salidas de un contrato de clase. Una prueba debe , solamente saber lo suficiente sobre la implementación para burlarse de los métodos que invoca en sus dependencias. Nada mas. Si no puede cambiar su implementación sin tener que cambiar una prueba, es probable que su estrategia de prueba sea deficiente. - colin m

@Colin M Eso no es realmente lo que está preguntando;) Déjalo decidir, no conoces el proyecto. - aarón marcus

No es realmente cierto. Podría requerir mucho esfuerzo probar una pequeña parte del método privado a través del método público que lo usa. El método público podría requerir una configuración significativa antes de llegar a la línea que llama a su método privado: LCA

Estoy de acuerdo. Debe probar, si el contrato se cumple. No debe probar cómo se hace esto. Si el contacto se cumple sin alcanzar el 100% de cobertura del código (en métodos privados), ese puede ser un código muerto o inútil. - wuppi

De este artículo: Prueba de métodos privados con JUnit y SuiteRunner (Bill Venners), básicamente tienes 4 opciones:

  1. No pruebes métodos privados.
  2. Dar acceso al paquete de métodos.
  3. Utilice una clase de prueba anidada.
  4. Usa la reflexión.

Respondido 23 Abr '20, 00:04

@JimmyT., Depende de para quién sea el "código de producción". Yo llamaría código de producción al código producido para aplicaciones ubicadas dentro de VPN cuyos usuarios objetivo son administradores de sistemas. - marcapasos

@Pacerier ¿A qué te refieres? - Jimmy T.

La quinta opción, como se mencionó anteriormente, ¿está probando el método público que llama al método privado? ¿Correcto? - user2441441

@LorenzoLerate He estado luchando con esto porque estoy haciendo desarrollo integrado y me gustaría que mi software sea lo más pequeño posible. Las limitaciones de tamaño y el rendimiento es la única razón en la que puedo pensar. - usuario2918461

Realmente me gusta la prueba usando la reflexión: Aquí el patrón Method method = targetClass.getDeclaredMethod (methodName, argClasses); method.setAccessible (verdadero); return method.invoke (targetObject, argObjects); - Águido

Generalmente, una prueba de unidad está destinada a ejercitar 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.

Respondido el 29 de diciembre de 12 a las 09:12

Esa es la mejor respuesta en mi opinión, o como se suele decir, prueba de comportamiento, no de métodos. Las pruebas unitarias no reemplazan las métricas del código fuente, las herramientas de análisis de código estático y las revisiones de código. Si los métodos privados son tan complejos que necesitan pruebas separadas, probablemente sea necesario refactorizarlo, no más pruebas. - Dan Haynes

Entonces, ¿no escribas métodos privados, solo creas otras 10 clases pequeñas? - maquinilla de afeitar

No, básicamente significa que puede probar la funcionalidad de los métodos privados utilizando el método público que los invoca. - Akshat Sharda

Solo dos ejemplos de dónde me gustaría probar un método privado:

  1. Rutinas de descifrado - No quisiera hacerlos visibles para que nadie los vea solo por el simple hecho de probarlos, de lo contrario, cualquiera puede usarlos para descifrar. Pero son intrínsecos al código, complicados y deben funcionar siempre (la excepción obvia es la reflexión, que se puede usar para ver incluso métodos privados en la mayoría de los casos, cuando SecurityManager no está configurado para evitar esto).
  2. Creando un SDK para consumo comunitario. Aquí, público adquiere un significado completamente diferente, ya que este es un código que todo el mundo puede ver (no solo interno de mi aplicación). Pongo código en métodos privados si no quiero que los usuarios del SDK lo vean; no veo esto como un olor a código, simplemente como cómo funciona la programación del SDK. Pero, por supuesto, todavía necesito probar mis métodos privados, y es donde reside realmente la funcionalidad de mi SDK.

Entiendo la idea de probar únicamente el "contrato". Pero no veo que uno pueda abogar realmente por no probar el código; su millaje puede variar.

Entonces, mi compensación consiste en complicar los JUnits con reflexión, en lugar de comprometer mi seguridad y SDK.

Respondido el 13 de enero de 17 a las 19:01

Si bien nunca debe hacer público un método solo para probarlo, private no es la única alternativa. Ningún modificador de acceso es package private y significa que puede realizar una prueba unitaria siempre que su prueba unitaria viva en el mismo paquete. - verde

Estaba comentando su respuesta, el punto 1 en particular, no el OP. No es necesario que un método sea privado solo porque no desea que sea público. - verde

@ngreen true thx - Fui vago con la palabra "público". He actualizado la respuesta para incluir público, protegido, predeterminado (y para hacer mención a la reflexión). El punto que estaba tratando de hacer es que hay una buena razón para que algún código sea secreto, sin embargo, eso no debería impedirnos probarlo. - Richard Le Mésurier

Gracias por dar ejemplos reales a todos. La mayoría de las respuestas son buenas, pero desde un punto de vista teórico. En el mundo real, necesitamos ocultar la implementación del contrato y aún tenemos que probarla. Al leer todo esto, creo que tal vez este debería ser un nivel completamente nuevo de prueba, con un nombre diferente: Bernardo Dal Corno

Un ejemplo más: Analizadores HTML de rastreadores. Tener que analizar una página html completa en objetos de dominio requiere una tonelada de lógica esencial para validar pequeñas partes de la estructura. Tiene sentido dividir eso en métodos y llamarlo desde un método público, pero no tiene sentido que todos los métodos más pequeños que lo componen sean públicos, ni tiene mucho sentido crear 10 clases por página HTML. - Inferior

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 un método público falla, podría ser una falla en el método privado.

Respondido 29 ago 08, 17:08

Hecho real. El problema es cuando la implementación es más complicada, como si un método público llama a varios métodos privados. ¿Cuál falló? O si es un método privado complicado, que no se puede exponer ni transferir a una nueva clase - Bernardo Dal Corno

Ya mencionaste por qué se debe probar el método privado. Dijiste "entonces podría ser", así que no estás seguro. En mi opinión, si un método debe probarse es ortogonal a su nivel de acceso. - Weiqiu

Otro enfoque que he utilizado es cambiar un método privado para empaquetar privado o protegido y luego complementarlo con el @VisibleParaPruebas anotación de la biblioteca de Google Guava.

Esto le dirá a cualquiera que use este método que tenga cuidado y no acceda directamente ni siquiera en un paquete. Además, una clase de prueba no necesita estar en el mismo paquete físicamente, pero en el mismo paquete bajo el test carpeta.

Por ejemplo, si un método que se va a probar está en src/main/java/mypackage/MyClass.java entonces su llamada de prueba debe colocarse en src/test/java/mypackage/MyClassTest.java. De esa manera, obtuvo acceso al método de prueba en su clase de prueba.

Respondido el 13 de enero de 17 a las 19:01

No sabía nada de este, es interesante, sigo pensando que si necesitas ese tipo de anotación tienes problemas de diseño. - seb

Para mí, esto es como decirle, en lugar de darle una llave única a su guardián de incendios para que pruebe el simulacro de incendio, deja la puerta principal abierta con un letrero en la puerta que dice: "desbloqueado para pruebas, si no es de nuestra empresa, por favor no ingrese ". - P. Jeremy Krieg

@FrJeremyKrieg - ¿qué significa eso? - maestrojoe

@ MasterJoe2: el propósito de una prueba de fuego es mejorar la seguridad de su edificio contra el riesgo de incendio. Pero tienes que darle acceso al alcaide a tu edificio. Si deja permanentemente la puerta principal sin llave, aumenta el riesgo de acceso no autorizado y lo hace menos a salvo. Lo mismo ocurre con el patrón VisibleForTesting: aumenta el riesgo de acceso no autorizado para reducir el riesgo de "incendio". Es mejor otorgar acceso como único para probar solo cuando lo necesite (por ejemplo, usando la reflexión) en lugar de dejarlo permanentemente desbloqueado (no privado). - P. Jeremy Krieg

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 ahora.

Yo uso el junitx.util.PrivateAccessor-paquete para Java. Un montón de frases útiles 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);

Respondido el 14 de diciembre de 17 a las 12:12

Asegúrese de descargar todos los complementos de JUnit (sourceforge.net/projects/junit-addons), no el Accessor privado del proyecto recomendado por Source Forge. - skia.heliou

Y asegúrese de que, cuando utilice un Class como primer parámetro en estos métodos, al que solo accede static miembros. - skia.heliou

En los Marco de primavera puede probar métodos privados usando este método:

ReflectionTestUtils.invokeMethod()

Por ejemplo:

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

Respondido el 13 de enero de 17 a las 19:01

La solución más limpia y concisa hasta ahora, pero solo si está utilizando Spring. - Denis Nikolaenko

Habiendo probado Cem Catikkas ' solución usando reflexión para Java, debo decir que la suya 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.

Hay un mérito posible en probar métodos privados de una clase, particularmente con desarrollo basado en pruebas, donde le gustaría diseñar pequeñas pruebas antes de escribir cualquier código.

La creación de una prueba con acceso a miembros y métodos privados puede probar áreas de código que son difíciles de apuntar específicamente con acceso solo a métodos públicos. Si un método público tiene varios pasos involucrados, puede constar de varios métodos privados, que luego pueden probarse individualmente.

Ventajas:

  • Puede probar con una granularidad más fina

Desventajas:

  • El código de prueba debe residir en el mismo archivo que el código fuente, que puede ser más difícil de mantener
  • De manera similar con los archivos de salida .class, deben permanecer dentro del mismo paquete que se declara en el código fuente

Sin embargo, si las pruebas continuas requieren este método, puede ser una señal de que se deben extraer los métodos privados, que podrían probarse de la manera pública tradicional.

Aquí hay un ejemplo complicado de cómo funcionaría esto:

// 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);
        }
    }
}

La clase interna se compilaría para ClassToTest$StaticInnerTest.

Ver también: Consejo 106 de Java: clases internas estáticas para divertirse y obtener ganancias

Respondido el 14 de diciembre de 17 a las 12:12

Como han dicho otros ... no pruebes métodos privados directamente. Aquí hay algunos pensamientos:

  1. Mantenga todos los métodos pequeños y enfocados (fáciles de probar, fáciles de encontrar lo que está mal)
  2. Utilice herramientas de cobertura de código. Me gusta Cobertura (¡Oh, feliz día, parece que ha salido una nueva versión!)

Ejecute la cobertura del código en las pruebas unitarias. Si ve que los métodos no están completamente probados, agregue 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á.

Respondido el 13 de enero de 17 a las 19:01

Preparado para la cobertura del código. No importa qué tipo de lógica haya en el método privado, todavía está invocando esa lógica a través de un método público. Una herramienta de cobertura de código puede mostrarle qué partes están cubiertas por la prueba, por lo tanto, puede ver si su método privado está probado. - coolcfan

He visto clases en las que el único método público es main [], y abren una GUI y conectan la base de datos y un par de servidores web en todo el mundo. Fácil de decir "no probar indirectamente" ... - Audrius Meskauskas

Los métodos privados son consumidos por los públicos. De lo contrario, son un 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.

Las pruebas de métodos privados deben probarse depurando antes de ejecutar sus pruebas unitarias en métodos públicos.

También pueden depurarse mediante el desarrollo basado en pruebas, depurando sus pruebas unitarias hasta que se cumplan todas sus afirmaciones.

Personalmente creo que es mejor crear clases usando TDD; creando los stubs del método público, luego generando pruebas unitarias con todos las las aserciones definidas de antemano, por lo que el resultado esperado del método se determina antes de codificarlo. De esta manera, no tomará el camino equivocado para hacer que las afirmaciones de la prueba unitaria se ajusten a los resultados. Entonces, su clase es sólida y cumple con los requisitos cuando pasan todas las pruebas unitarias.

Respondido el 13 de enero de 17 a las 19:01

Aunque esto es cierto, puede conducir a algunas pruebas muy complicadas; es un patrón mejor probar un solo método a la vez (prueba unitaria) en lugar de un grupo completo de ellos. No es una sugerencia terrible, pero creo que aquí hay mejores respuestas: este debería ser un último recurso. - Bill K

Si usa Spring, ReflectionTestUtils proporciona algunas herramientas útiles que ayudan aquí con un mínimo esfuerzo. Por ejemplo, para configurar un simulacro de un miembro privado sin verse obligado a agregar un establecedor público no deseado:

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

respondido 10 nov., 14:23

Si está intentando probar un código existente que no está dispuesto a cambiar o que no puede cambiar, la reflexión es una buena opción.

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

Si desea probar condiciones de error difíciles que provienen 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 las excepciones del ayudante. Este enfoque también es útil si desea probar la clase original sin probar también la clase auxiliar.

Respondido el 13 de enero de 17 a las 19:01

Probar métodos privados rompe la encapsulación de su clase porque cada vez que cambia la implementación interna rompe el código del cliente (en este caso, las pruebas).

Así que no pruebes métodos privados.

Respondido el 29 de diciembre de 12 a las 10:12

la prueba unitaria y el código src son un par. Si cambia el src, tal vez tenga que cambiar la prueba unitaria. Ese es el sentido de la prueba junit. Garantizarán que todo funciona como antes. y está bien si se rompen, si cambia el código. - Alex Wien

No esté de acuerdo con que la prueba unitaria deba cambiar si cambia el código. Si se le ordena agregar funcionalidad a una clase sin cambiar la funcionalidad existente de la clase, entonces las pruebas unitarias existentes deben aprobarse incluso después de haber cambiado su código. Como menciona Peter, las pruebas unitarias deben probar la interfaz, no cómo se hace internamente. En el desarrollo basado en pruebas, las pruebas unitarias se crean antes de que se escriba el código, para centrarse en la interfaz de la clase, no en cómo se resuelve internamente. - harald coppoolse

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

Pero si debes ...

Si está utilizando JDK 1.3 o superior, puede utilizar la reflexión para subvertir el mecanismo de control de acceso con la ayuda del Accesor privilegiado. Para obtener detalles sobre cómo usarlo, lea este artículo.

Si está usando JDK 1.6 o superior y anota sus pruebas con @Test, puede usar Dp4j para inyectar reflexión en sus métodos de prueba. Para obtener detalles sobre cómo usarlo, consulte este script de prueba.

PD: soy el principal contribuyente a Dp4j, pedir me si necesitas ayuda. :)

contestado el 23 de mayo de 17 a las 12:05

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, lo que le permitirá crear simulaciones de un objeto incluso cuando sean privados para la clase.

Respondido el 14 de diciembre de 17 a las 12:12

Como parte de la biblioteca jmockit, tiene acceso a la clase Deencapsulation que facilita la prueba de métodos privados: Deencapsulation.invoke(instance, "privateMethod", param1, param2); - Domenico D.

Utilizo este enfoque todo el tiempo. Muy útil - Curandero

Tiendo a no probar métodos privados. Ahí está la locura. Personalmente, creo que solo debe probar sus interfaces expuestas públicamente (y eso incluye métodos protegidos e internos).

Respondido 29 ago 08, 17:08

Si está usando JUnit, eche un vistazo a junit-complementos. Tiene la capacidad de ignorar el modelo de seguridad de Java y acceder a métodos y atributos privados.

Respondido el 14 de diciembre de 17 a las 12:12

Te sugiero que refactorices un poco tu código. Cuando tienes que empezar a pensar en usar la reflexión u otro tipo de cosas, simplemente para probar tu código, algo va mal con tu código.

Mencionaste diferentes tipos de problemas. Empecemos por los campos privados. En el caso de los campos privados, habría agregado un nuevo constructor e inyectado campos en eso. En lugar de esto:

public class ClassToTest {

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

Habría usado esto:

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 algunos códigos heredados. El código antiguo 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 reflejos.

Ahora sobre métodos privados. En mi experiencia personal, cuando tienes que fragmentar un método privado para realizar pruebas, 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 pasa esa interfaz también en el constructor (con ese truco de constructor múltiple):

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

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

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 de las clases anidadas. Si una clase anidada contiene 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. Usar la reflexión principalmente 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 simplemente molesto y no me gusta ese tipo de cosas).

También hay inyección de setter, pero no recomendaría usarla. Será mejor que me quede con un constructor e inicialice todo cuando sea realmente necesario, dejando la posibilidad de inyectar las dependencias necesarias.

Respondido el 13 de enero de 17 a las 20:01

No estoy de acuerdo con el ClassToTest(Callable) inyección. Lo que hace ClassToTest más complicado. Mantenlo simple. Además, eso requiere otra persona para contar ClassToTest algo que ClassToTest debería ser el jefe de. Hay un lugar para inyectar lógica, pero no es este. Acabas de hacer que la clase sea más difícil de mantener, no más fácil. - Loduwijk

Además, es preferible que su método de prueba X no aumenta X complejidad hasta el punto en que podría causar más problemas ... y, por lo tanto, requerir pruebas adicionales ... que si ha implementado de una manera que puede causar más problemas ... (esto no es un bucle infinito; es probable que cada iteración más pequeño que el anterior, pero aún molesto) - Loduwijk

¿Podrías explicar por qué haría clase? 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á en first y 'segundas' variables? - GROX13

El método de prueba no aumenta la complejidad. Es solo tu clase la que está mal escrita, tan mal que no se puede probar. - GROX13

Más difícil de mantener porque la clase es más complicada. class A{ A(){} f(){} } es más simple que class A { Logic f; A(logic_f) } class B { g() { new A( logic_f) } }. Si eso no fuera cierto, y si fuera cierto que proporcionar la lógica de una clase como argumentos de constructor fuera más fácil de mantener, entonces pasaríamos toda la lógica como argumentos de constructor. Simplemente no veo cómo puedes reclamar class B{ g() { new A( you_dont_know_your_own_logic_but_I_do ) } } siempre hace que A sea más fácil de mantener. Hay casos en los que inyectar de esta manera tiene sentido, pero no veo su ejemplo como "más fácil de mantener". Loduwijk

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 de destino desde cualquier 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. Entonces, probar un método protegido de una clase de destino significa que necesitamos definir su clase de prueba en el mismo paquete que la clase de destino.

Si todo lo anterior no se adapta a sus necesidades, utilice el camino de la reflexión para acceder al método privado.

contestado el 23 de mayo de 17 a las 13:05

Estás mezclando "protegido" con "amistoso". Un método protegido solo puede ser accedido por una clase cuyos objetos son asignables a la clase de destino (es decir, subclases). - sayo oladeji

En realidad, no hay "Amistoso" en Java, el término es "Paquete" y se indica por la falta de un modificador privado / público / protegido. Habría corregido esta respuesta, pero hay una muy buena que ya dice esto, por lo que recomendaría eliminarla. - Bill K

Casi voté en contra, pensando todo el tiempo "Esta respuesta es simplemente incorrecta". hasta que llegué a la última oración, que contradice el resto de la respuesta. La última oración debería haber sido la primera. - Loduwijk

Como muchos han sugerido anteriormente, una buena forma 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 de hecho se están ejecutando a partir de sus pruebas.

Respondido el 10 de Septiembre de 08 a las 08:09

¡No debe probar indirectamente! No solo tocar a través de la cobertura; compruebe que se obtiene el resultado esperado! - Alex Wien

Aquí está mi función genérica para probar 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);
}

Respondido el 13 de enero de 17 a las 19:01

Vea a continuación un ejemplo;

Se debe agregar la siguiente declaración de importación:

import org.powermock.reflect.Whitebox;

Ahora puede pasar directamente el objeto que tiene el método privado, el nombre del método que se llamará y los parámetros adicionales como se muestra a continuación.

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

Respondido el 13 de enero de 17 a las 19:01

Hoy, impulsé una biblioteca de Java para ayudar a probar métodos y campos privados. Ha sido diseñado pensando en Android, pero realmente se puede utilizar 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 busca. A continuación, se muestra un ejemplo de una prueba que accede a dos campos privados de una actividad de Android para probarla:

@UiThreadTest
public void testCompute() {

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

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

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

BoundBox facilita la prueba de campos, métodos y constructores privados / protegidos. Incluso puede 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 comprueba en tiempo de compilación.

Es ideal para probar algunos códigos heredados. Úselo con cuidado. ;)

https://github.com/stephanenicolas/boundbox

Respondido el 14 de diciembre de 17 a las 12:12

¡Lo acabo de probar, BoundBox es una solución simple y elegante! - porhas

Primero, arrojaré esta pregunta: ¿Por qué sus miembros privados necesitan pruebas aisladas? ¿Son tan complejos, proporcionando comportamientos tan complicados que requieren pruebas fuera 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 cada uno de estos miembros privados sea una 'unidad' de gran complejidad, considere la posibilidad de refactorizar dichos 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 encuentra bajo la prueba unitaria? Bajo la prueba unitaria, la estrategia proporcionaría una validación adicional, pero en las versiones de lanzamiento sería un simple traspaso.

Respondido el 13 de enero de 17 a las 19:01

Porque a menudo un fragmento particular de código de un método público se refactoriza en un método privado interno y es realmente el fragmento crítico de la lógica en la que podría haberse equivocado. Quieres probar esto independientemente del método público: oxbow_lakes

Incluso el código más corto a veces sin prueba unitaria no es correcto. Intente calcular la diferencia entre 2 ángulos geográficos. 4 líneas de código, y la mayoría no lo hará correctamente al primer intento. Tales métodos necesitan una prueba unitaria, porque forman la base de un código confiable. (Respuesta: este código útil también puede ser público; menos útil protegido - Alex Wien

PowerMockito está hecho para esto. Utilice la dependencia de maven

    <dependency>
        <groupId>org.powermock</groupId>
        <artifactId>powermock-core</artifactId>
        <version>2.0.7</version>
        <scope>test</scope>
    </dependency>

Entonces puedes hacer

import org.powermock.reflect.Whitebox;
...
MyClass sut = new MyClass();
SomeType rval = Whitebox.invokeMethod(sut, "myPrivateMethod", params, moreParams);

Respondido 02 Jul 20, 19:07

Recientemente tuve este problema y escribí una pequeña herramienta, llamada Ganzúa, que evita los problemas de usar explícitamente la API de reflexión de Java, dos ejemplos:

Métodos de llamada, p. Ej. private void method(String s) - por reflexión de Java

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

Métodos de llamada, p. Ej. private void method(String s) - por Picklock

interface Accessible {
  void method(String s);
}

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

Configurar campos, p. Ej. private BigInteger amount; - por reflexión de Java

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

Configurar campos, p. Ej. private BigInteger amount; - por Picklock

interface Accessible {
  void setAmount(BigInteger amount);
}

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

Respondido el 13 de enero de 17 a las 19:01

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.