2010-07-28 7 views
15

En muchas aplicaciones, a menudo tengo algoritmos que hacen uso de sub-algoritmos dedicados (o simplemente fragmentos de código bien definidos).¿Cómo pruebo los métodos internos de la clase interna en Java?

Hasta ahora, cuando escribí el algoritmo principal, he creado un método privado para cada sub-algoritmo, como en el ejemplo siguiente (OldStyle):

public class OldStyle { 

    public int mainAlg() { 
     int x = subAlg01(); 
     int y = subAlg02(); 
     int z = x * y; 
     return z; 
    } 

    private int subAlg01() { 
     return 3; 
    } 

    private int subAlg02() { 
     return 5; 
    } 
} 

Esto funcionó bien, pero no me gustó tener una proliferación de métodos (subAlg01 y subAlg02) que, incluso si eran privados, solo se usaban por un método (mainAlg).

Recientemente dicovered el uso de clases internas locales y ahora mi ejemplo es (NewStyle):

public class NewStyle { 

    public int mainAlg() { 
     class Nested { 

      public int subAlg01() { 
       return 3; 
      } 

      public int subAlg02() { 
       return 5; 
      } 
     } 
     Nested n = new Nested(); 
     int x = n.subAlg01(); 
     int y = n.subAlg02(); 
     int z = x * y; 
     return z; 
    } 
} 

me gusta mucho, pero ahora tengo el siguiente problema: ¿Cómo se prueba subAlg01 y subAlg02 usando JUnit?

Por cierto: estoy usando eclipse.

Gracias por su ayuda.

Editar: Intento explicar mejor: tengo, digamos, un algoritmo de clasificación y quiero probarlo para asegurarme de que funciona como se esperaba. Este algoritmo de clasificación es utilizado solo por el método m de la clase X. Podría convertirlo en un método privado de clase X, pero la clase X generalmente no tiene nada que ver con la ordenación, entonces ¿por qué "echar a perder" la clase X con el método de clasificación? Entonces lo puse dentro del método m. Algún tiempo después, quiero mejorar mi algoritmo de clasificación (lo hago más rápido), pero quiero asegurarme de que su comportamiento sea el esperado, por lo que quiero volver a probarlo con las pruebas originales.

Eso es lo que quiero hacer, quizás no haya solución, espero que alguien me pueda ayudar.

Editar después de la elección de respuesta. Seleccioné la respuesta de Rodney porque su solución fue la que adopté: una clase de ayudante independiente me ayuda (¡es una ayuda!) A tener una visión clara de cuáles son los métodos secundarios y también me da la capacidad de probarlos.

+0

I eliminó la etiqueta de Eclipse ya que este problema no es específico de ella. –

Respuesta

10

Filippo, entiendo su frustración con el problema y con algunas de las respuestas. Cuando comencé a usar JUnit hace muchos años, yo también quería probar código privado, y pensé que era tonto de los gurús decir que era una mala idea.

Resulta que tenían razón (¡sorpresa!) Pero solo después de escribir bastantes pruebas entendí por qué. Es posible que tenga que pasar por el mismo proceso, pero finalmente llegará a la misma conclusión ;-)

De todos modos, en su situación, haría Nested en una clase independiente adecuada, posiblemente en un paquete separado para hacer obvio que es una clase de ayuda. Luego, escribiría pruebas directamente, independientemente de cualquier otra prueba.

Luego escribo las pruebas para NewStyle y me concentro solo en el comportamiento de NewStyle.

(Muy probablemente me Nested también se inyecta en NewStyle en lugar de crear instancias dentro NewStyle -. Es decir, que sea un argumento para el constructor NewStyle 's

Entonces cuando escribo pruebas para NewStyle en la prueba que había pasar en una instancia de Nested y seguir adelante. Si me sentí particularmente difícil, me gustaría crear una interfaz de Nested y crear una segunda aplicación, y la prueba NewStyle con eso, también.)

+0

+1 porque su solución es la misma que adopté actualmente (clase de ayudante independiente) –

16

Solo debe probar la interfaz pública de clases, no miembros privados o clases internas privadas. Los miembros privados están destinados a ser detalles de implementación, utilizados solo por los métodos públicos de la clase (directa o indirectamente). Por lo tanto, puede probarlos de forma individual a través de sus métodos de llamada. Si siente que no tiene suficiente granularidad en esas pruebas unitarias, o que no puede detectar (algunos de) los resultados que le interesan, esto probablemente signifique un problema con su diseño: la clase puede ser demasiado grande, intentarlo hacer demasiado, por lo tanto, es posible que deba extraerse parte de su funcionalidad en una clase separada, donde luego puede someterse a pruebas unitarias directamente.

En el ejemplo actual, si la clase interna contiene mucho código, puede simplemente convertirlo en una clase de nivel superior, luego puede probar sus métodos directamente.

(BTW su clase interna debe ser static si no necesita hacer referencia a la instancia de clase envolvente.)

+0

Hola, gracias por la respuesta. Ya leí muchas respuestas como la tuya, pero no tengo muy claro el significado. Por favor, ¿puedes ayudarme a entender mejor? Tengo un código que se usa solo una vez, pero necesito verificar si cumple con lo que se espera que haga. ¿Lo logro? Las pruebas me parecieron una buena elección. –

+0

@Filippo, mira mi actualización. Las pruebas son realmente una buena opción: escribir pruebas que cubran todos los diferentes escenarios de uso que se te puedan ocurrir. Una herramienta de cobertura de código lo ayuda a detectar partes de código no probado. –

+1

@ Péter acerca de su sugerencia sobre estática: las clases internas pueden ser estáticas clase interna local no –

1

No se puede llegar a estas clases desde el exterior, por lo que no se puede junit probarlos. Debes tener cosas públicas para probarlos.

+0

Esto no es correcto. Los frameworks falsos como JMockit pueden manejar clases internas privadas (así como también métodos privados y miembros). – jchilders

+1

JMockit probablemente hace cosas malas con la reflexión entonces, que junit no hace. –

+0

¿Sabe si JMockit (o marcos similares) puede probar métodos dentro de las clases internas locales? –

1

Usted debe evitar el exceso de complicar su código sólo porque

no les gusta tener una proliferación de métodos

En su caso, usted puede probar los métodos si son métodos de la clase externa o si realmente necesita la clase interna, colóquela fuera del método mainAlg() para que esté visible globalmente.

public class NewStyle { 

    public int mainAlg() { 

     Nested n = new Nested(); 
     int x = n.subAlg01(); 
     int y = n.subAlg02(); 

     int z = x * y; 
     return z; 
    } 

    class Nested { 

     public int subAlg01() { 
      return 3; 

     } 

     public int subAlg02() { 
      return 5; 
     } 
    } 
} 

esto, entonces puede ser llamado usando new NewStyle().new Nested().subAlg01();

+0

Gracias por la respuesta. También probé esta solución, pero me gusta menos que la mía, ya que, mirando la clase, todavía no tengo una visión clara de que los subAlgs son solo parte del algoritmo principal y no son utilizados por nadie más. Sin embargo, permite las pruebas. –

+0

nuevo NewStyle(). Nuevo Anidado(). SubAlg01(); ¿Estás seguro? NUNCA HE visto esto .../reflexiono –

1

Hm, sé que lenguas como el maravilloso menudo se utilizan para hacer las pruebas unitarias en Java, y son capaces de acceder a los campos privados y métodos transparente .... .pero no estoy seguro acerca de las clases anidadas. Puedo entender por qué es posible que desee hacer este tipo de cosas, pero en cierto modo adopto la misma posición que tomo con los evaluadores de búsqueda y configuración, deben probarse como un efecto secundario de probar sus métodos públicos, y si no lo hacen hacerse la prueba de esa manera, entonces ¿por qué están allí para empezar?

1

El problema viene cuando su interior el comportamiento de la clase pasa a ser una parte central de wh en un método: Diga que tiene un método que se supone que pasa un ejecutable a un tercer objeto, que resulta ser una clase interna que realmente no debería ser una entidad separada: en este caso, la prueba de unidad adecuada requiere mucho ejercicio Dicha clase interna burlándose de ese tercer objeto, y usando la captura de argumento para probarlo, hace lo que debería.

Este tipo de problemas son muy comunes en los lenguajes que tienen más soporte para la programación funcional, donde probar lambdas pasado a terceros es casi un requisito.

Pero, como se dijo antes, si su clase interna nunca es utilizada por un tercero, entonces es solo un detalle de implementación, y probarla por separado simplemente no es útil.

1

En realidad, puede probar su clase interna local. Pero necesita implementar una interfaz. De esta forma, puede crear una instancia de la clase local a través de la reflexión y convertirla en su interfaz. Una vez que tenga el tipo de interfaz, se puede probar el código de ejecución sin problemas:

La interfaz:

public interface Algorithm { 
    int subAlg01(); 
    int subAlg02(); 
} 

La clase:

public class NewStyle { 
    public int mainAlg() { 
     class Nested implements Algorithm { 

      public int subAlg01() { 
       return 3; 
      } 

      public int subAlg02() { 
       return 5; 
      } 
     } 
     Nested n = new Nested(); 
     int x = n.subAlg01(); 
     int y = n.subAlg02(); 
     int z = x * y; 
     return z; 
    } 
} 

La prueba:

public class NewStyleTest { 

    @Test 
    public void testLocal() throws ClassNotFoundException, 
      NoSuchMethodException, SecurityException, InstantiationException, 
      IllegalAccessException, IllegalArgumentException, 
      InvocationTargetException { 
     Class<?> forName = Class.forName("NewStyle$1Nested"); 
     Constructor<?> declaredConstructor = forName 
       .getDeclaredConstructor(NewStyle.class); 
     declaredConstructor.setAccessible(true); 
     Algorithm algorithm = (Algorithm) declaredConstructor 
       .newInstance(new NewStyle()); 

     assertEquals(algorithm.subAlg01(), 3); 
     assertEquals(algorithm.subAlg02(), 5); 
    } 
} 
Cuestiones relacionadas