2012-06-18 7 views
8

Quiero ejecutar las mismas pruebas JUnit para diferentes implementaciones de interfaz. He encontrado una buena solución con la opción @parameter:Prueba de implementaciones de múltiples interfaces con las mismas pruebas: JUnit4

public class InterfaceTest{ 

    MyInterface interface; 

    public InterfaceTest(MyInterface interface) { 
    this.interface = interface; 
    } 

    @Parameters 
    public static Collection<Object[]> getParameters() 
    { 
    return Arrays.asList(new Object[][] { 
     { new GoodInterfaceImpl() }, 
     { new AnotherInterfaceImpl() } 
    }); 
    } 
} 

Esta prueba se ejecuta dos veces, primero con el GoodInterfaceImpl luego con la claseAnotherInterfaceImpl. Pero el problema es que necesito para la mayoría de las pruebas un nuevo objeto. Un ejemplo simplificado:

@Test 
public void isEmptyTest(){ 
    assertTrue(interface.isEmpty()); 
} 

@Test 
public void insertTest(){ 
    interface.insert(new Object()); 
    assertFalse(interface.isEmpty()); 
} 

Si el isEmptyTest se ejecuta después de la insertTest falla.

¿Existe la opción de ejecutar automáticamente cada caso de prueba con una nueva instancia de una implementación?

Por cierto: La implementación de un claro() o reset() -method para la interfaz no es realmente una opciones ya que no lo necesitaría en el código productiva.

+0

Sugiero mirar en la inyección de dependencia. Aquí hay un pequeño artículo sobre cómo usarlo para lograr exactamente lo que está buscando http://www.javaranch.com/journal/200709/dependency-injection-unit-testing.html –

+0

Gracias por su respuesta rápida ! Pero este artículo describe cómo romper dependencias en una interfaz. Quiero probar la interfaz (no la clase que usa la interfaz, o para hablar en términos del artículo: quiero probar la granja remota [los motores], no el servlet de granja [los carros]). –

Respuesta

4

Cree una interfaz e implementaciones de fábrica, posiblemente solo en su jerarquía de pruebas si no necesita tal cosa en producción, y haga que getParameters() devuelva una lista de fábricas.

Luego puede invocar la fábrica en un método anotado @Before para obtener una nueva instancia de su clase real bajo prueba para cada ejecución del método de prueba.

+0

¡Muchas gracias! Esa es una solución inteligente, implementaré eso :) –

5

Aquí es otro enfoque con el patrón Método Plantilla:

Las pruebas orientada interfaz de entrar en la clase base:

public abstract class MyInterfaceTest { 

    private MyInterface myInterface; 

    protected abstract MyInterface makeContractSubject(); 

    @Before 
    public void setUp() { 
     myInterface = makeContractSubject(); 
    } 

    @Test 
    public void isEmptyTest(){ 
     assertTrue(myInterface.isEmpty()); 
    } 

    @Test 
    public void insertTest(){ 
     myInterface.insert(new Object()); 
     assertFalse(myInterface.isEmpty()); 
    } 
} 

Para cada clase concreta, definir una clase de prueba de hormigón:

public class GoodInterfaceImplTest extends MyInterfaceTest { 

    @Override 
    protected MyInterface makeContractSubject() { 
     // initialize new GoodInterfaceImpl 
     // insert proper stubs 
     return ...; 
    } 

    @Test 
    public void additionalImplementationSpecificStuff() { 
     ... 
    } 
} 

Una ligera ventaja con respecto a @Parameter es que obtiene el nombre de la clase de prueba concreta informada cuando falla una prueba, para que sepa de inmediato qué implementación falló.

Por cierto, para que este enfoque funcione, la interfaz debe diseñarse de forma tal que solo permita la prueba por los métodos de interfaz. Esto implica pruebas basadas en estado: no se pueden verificar burlas en la clase de prueba base. Si necesita verificar simulaciones en pruebas específicas de implementación, estas pruebas deben ir a las clases de prueba concretas.

+0

+1 para otro buen enfoque :) Pero con respecto a los nombres de clase, ahora es posible (JUnit> = 4.11) dar diferentes nombres a las pruebas, aunque no es muy útil para usar -> http://stackoverflow.com/questions/650894/changing-names-of-parameterized-tests (es decir, la respuesta mejor calificada, no la aceptada). Aún así, me gusta su solución, porque puede agregar pruebas específicas de implementación en clases de prueba derivadas. Algo que necesito una clase extra de lo contrario. –

1

Sólo en caso de que alguien llega aquí (como yo), en busca de pruebas de múltiples implementaciones de la misma interfaz de .NET se podía ver uno de los enfoques que estaba usando en uno de los proyectos here

Abajo es lo que estamos siguiendo en pocas palabras

El mismo proyecto de prueba dll se ejecuta dos veces usando vstest.console, estableciendo una variable de entorno. Dentro de la prueba, (ya sea en la inicialización del ensamblaje o en la inicialización de la prueba), registre las implementaciones apropiadas en un contenedor IoC, en función del valor de la variable de entorno.

0

En Junit 5 que podría hacer:

@ParameterizedTest 
@MethodSource("myInterfaceProvider") 
void test(MyInterface myInterface) {} 

static Stream<MyInterface> myInterfaceProvider() { 
    return Stream.of(new ImplA(), new ImplB()); 
} 

interface MyInterface {} 

static class ImplA implements MyInterface {} 

static class ImplB implements MyInterface {} 
Cuestiones relacionadas