2010-06-08 18 views
14

Así que he empezado a disposición de las pruebas unitarias para el siguiente trozo de código:Unidad de Pruebas de Arquitectura Pregunta

public interface MyInterface { 
    void MyInterfaceMethod1(); 
    void MyInterfaceMethod2(); 
} 

public class MyImplementation1 implements MyInterface { 
    void MyInterfaceMethod1() { 
    // do something 
    } 

    void MyInterfaceMethod2() { 
    // do something else 
    } 

    void SubRoutineP() { 
    // other functionality specific to this implementation 
    } 
} 

public class MyImplementation2 implements MyInterface { 
    void MyInterfaceMethod1() { 
    // do a 3rd thing 
    } 

    void MyInterfaceMethod2() { 
    // do something completely different 
    } 

    void SubRoutineQ() { 
    // other functionality specific to this implementation 
    } 
} 

con varias implementaciones y la expectativa de más por venir.

Mi primer pensamiento fue para salvarme pruebas unitarias re-escritura de tiempo con algo como esto:

public abstract class MyInterfaceTester { 
    protected MyInterface m_object; 

    @Setup 
    public void setUp() { 
    m_object = getTestedImplementation(); 
    } 

    public abstract MyInterface getTestedImplementation(); 

    @Test 
    public void testMyInterfaceMethod1() { 
    // use m_object to run tests 
    } 

    @Test 
    public void testMyInterfaceMethod2() { 
    // use m_object to run tests 
    } 
} 

la que entonces podría fácilmente en subclases para poner a prueba la aplicación de métodos adicionales específicos de esta manera:

public class MyImplementation1Tester extends MyInterfaceTester { 
    public MyInterface getTestedImplementation() { 
    return new MyImplementation1(); 
    } 

    @Test 
    public void testSubRoutineP() { 
    // use m_object to run tests 
    } 
} 

y también para la implementación 2 en adelante.

Así que mi pregunta es realmente: ¿hay alguna razón para no hacer esto? A JUnit parece gustarle, y satisface mis necesidades, pero realmente no he visto nada igual en ninguno de los libros de prueba de unidades y ejemplos que he estado leyendo.

¿Hay alguna práctica recomendada que estoy violando sin querer? ¿Me estoy preparando para un dolor de cabeza en el camino? ¿Simplemente hay una manera mucho mejor por ahí que no he considerado?

Gracias por cualquier ayuda.

Respuesta

17

¿Hay alguna razón para no hacer esto?

No. Do it. Las pruebas son clases para exactamente esta razón.

No he visto nada igual en ninguno de los libros de prueba de unidades y ejemplos que he estado leyendo.

Seguir leyendo. Las presentaciones no cubren esto.

¿Hay alguna práctica recomendada que esté infringiendo involuntariamente?

Am I establecimiento de mí mismo para dolores de cabeza en el camino?

Algunas personas se ponen nerviosos acerca de "pruebas frágiles". Aquí puede encontrar algunas preguntas buscando formas de hacerlo, de modo que un cambio en el software tampoco conlleva cambios en las pruebas. A la larga, intentar crear pruebas "robustas" es una tontería. Desea que se escriban pruebas para que cada pequeño cambio al nivel de interfaz visible del software requiera la reescritura de la prueba.

Desea realizar pruebas para que los cambios internos invisibles no requieran la reescritura de la prueba.

El uso de clases y subclases es ortogonal a esas consideraciones.

¿Hay simplemente una manera mucho mejor que no he considerado?

No. Orientación del objeto es el punto. Las pruebas son una clase por exactamente esta razón.

5

Aunque apoyo SLott 100% también consideraría JUnit parametrizar las pruebas en lugar de la jerarquía de clases de prueba para esto:

@RunWith(Parameterized.class) 
public class MyInterfaceTester { 
    private MyInterface m_object; 

    public void MyInterfaceTester(MyInterface object) { 
    m_object = object; 
    } 

    @Parameters 
    public static Collection<Object[]> data() { 
    List<Object[]> list = new ArrayList<Object[]>(); 

    list.add(new Object[]{new MyImplementation1()}); 
    list.add(new Object[]{new MyImplementation2()}); 

    return list; 
    } 

    @Test 
    public void testMyInterfaceMethod1() { 
    // use m_object to run tests 
    } 

    @Test 
    public void testMyInterfaceMethod2() { 
    // use m_object to run tests 
    } 
} 

No hay necesidad de jerarquía de clases de prueba: sólo tiene que añadir nueva aplicación mediante la adición de otro elemento de la lista en data método.

+0

No sabía nada de esta característica, gracias por compartirla. Más información, y un ejemplo de trabajo aquí: http://www.devx.com/Java/Article/31983/0/page/3 para cualquier otra persona interesada. –

1

Si realmente está haciendo la misma configuración y derribando en cada clase de prueba, entonces lo que está haciendo está bien, pero me parece que en la práctica esto casi nunca es el caso. De hecho, la mayor parte del tiempo tiene un método de configuración que ejemplifica los datos de prueba, como lo ha hecho aquí, ni siquiera es lo que desea. En cambio, dentro de una clase de prueba configura cualquier infraestructura, y cada método de prueba establece su propia instancia de un objeto para probar algún aspecto de la misma.

+0

Es cierto, mover la llamada getTestedImplementation() a la primera línea de cada prueba, y poner el resultado en un local puede ser lo que quiero en la línea. Probablemente sea más seguro comenzar con un nuevo objeto. Ahora mismo, sin embargo, no hay ningún estado en las clases de implementación, por lo que solo deberían crearse una vez. –