2011-10-03 7 views
10

Tengo una clase que es un conjunto JUnit de clases de prueba JUnit. Me gustaría definir una regla en la suite de que hacer algo para la base de datos antes y después de cada unidad de prueba se ejecuta si un determinado anotación está presente en ese método de ensayo.¿Cómo se define una regla de método JUnit en un banco de pruebas?

he sido capaz de crear un @ClassRule en las suites y las clases de prueba que va a hacer esto antes de cada clase (que no es lo suficientemente bueno) y he sido capaz de definir las reglas de prueba con el probar las clases ellos mismos, pero esto es repetitivo y no parece muy SECO.

¿Es posible definir una regla de método por prueba en el conjunto de aplicaciones o debo agregarlas a todas y cada una de las pruebas?

Editar: Para aclarar, quiero declarar el código en un conjunto que se ejecutará entre (es decir, "alrededor") de los métodos de prueba en las clases de prueba.

Respuesta

11

Esto se puede hacer, pero necesita un poco de trabajo. También debe definir su propio corredor de Suite y su propio Corredor de Prueba, y luego anular runChild() en el corredor de prueba. El uso de las siguientes clases Suite y de prueba:

@RunWith(MySuite.class) 
@SuiteClasses({Class1Test.class}) 
public class AllTests { 
} 

public class Class1Test { 
    @Deprecated @Test public void test1() { 
     System.out.println("" + this.getClass().getName() + " test1"); 
    } 

    @Test public void test2() { 
     System.out.println("" + this.getClass().getName() + " test2"); 
    } 
} 

Tenga en cuenta que he anotada test1() con @Deprecated. ¿Quieres hacer algo diferente cuando se tiene la anotación @Deprecated en la prueba, por lo que necesita extender Suite para utilizar una costumbre Runner:

public class MySuite extends Suite { 
    // copied from Suite 
    private static Class<?>[] getAnnotatedClasses(Class<?> klass) throws InitializationError { 
     Suite.SuiteClasses annotation = klass.getAnnotation(Suite.SuiteClasses.class); 
     if (annotation == null) { 
      throw new InitializationError(String.format("class '%s' must have a SuiteClasses annotation", klass.getName())); 
     } 
     return annotation.value(); 
    } 

    // copied from Suite 
    public MySuite(Class<?> klass, RunnerBuilder builder) throws InitializationError { 
     super(null, getRunners(getAnnotatedClasses(klass))); 
    } 

    public static List<Runner> getRunners(Class<?>[] classes) throws InitializationError { 
     List<Runner> runners = new LinkedList<Runner>(); 

     for (Class<?> klazz : classes) { 
      runners.add(new MyRunner(klazz)); 
     } 

     return runners; 
    } 
} 

JUnit crea una Runner para cada prueba se ejecutará. Normalmente, Suite simplemente crearía el predeterminado BlockJUnit4ClassRunner, todo lo que estamos haciendo aquí es anular el constructor para el conjunto que lee las clases de la anotación SuiteClass y estamos creando nuestros propios corredores con ellos, MyRunner. Esta es nuestra clase MyRunner:

public class MyRunner extends BlockJUnit4ClassRunner { 
    public MyRunner(Class<?> klass) throws InitializationError { 
     super(klass); 
    } 

    @Override 
    protected void runChild(final FrameworkMethod method, RunNotifier notifier) { 
     Description description= describeChild(method); 
     if (method.getAnnotation(Ignore.class) != null) { 
      notifier.fireTestIgnored(description); 
     } else { 
      if (description.getAnnotation(Deprecated.class) != null) { 
       System.out.println("name=" + description.getMethodName() + " annotations=" + description.getAnnotations()); 
      } 
      runLeaf(methodBlock(method), description, notifier); 
     } 
    } 
} 

mayor parte de esta se copia de BlockJUnit4ClassRunner.El bit He añadido es:

if (description.getAnnotation(Deprecated.class) != null) { 
    System.out.println("name=" + description.getMethodName() + " annotations=" + description.getAnnotations()); 
} 

donde probamos la existencia de la @Deprecated anotación en el método, y hacer algo si está allí. El resto queda como ejercicio para el lector. Cuando ejecuto la suite superior, consigo como salida:

name=test1 annotations=[@java.lang.Deprecated(), @org.junit.Test(expected=class org.junit.Test$None, timeout=0)] 
uk.co.farwell.junit.run.Class1Test test1 
uk.co.farwell.junit.run.Class1Test test2 

Tenga en cuenta que Suite tiene varios constructores dependiendo de la forma en que se invoca. Lo anterior funciona con Eclipse, pero no he probado otras formas de ejecutar la Suite. Vea los comentarios junto con los diversos constructores para Suite para más información.

-2

Puede group tests con TestNG. Y puede configurar TestNG para ejecutar un poco de lógica @BeforeGroup and @AfterGroup.

+0

No creo que sea eso lo que estoy buscando. Quiero algo que se ejecutará entre cada prueba. – ArtB

+0

Pero si tiene un grupo de pruebas, debería ser posible utilizar BeforeMethod y AfterMethod. (Supongo) – ollins

+2

Tal vez, independientemente, necesito usar JUnit y TestNG no es una opción. – ArtB

2

Puede usar un RunListener que agregue a la Suite. No le proporciona todo lo que una Regla puede hacer, pero le proporcionará una clase de Descripción que debería tener las anotaciones disponibles. Al menos, no creo que JUnit lo filtre solo a sus anotaciones entendidas.

El desarrollador de JUnit acaba de discutir la mecánica de agregar un RunListener a una Suite here.

1

Por sí solo, agregar una regla a la clase anotada con @RunWith(Suite.class) no servirá. Creo que esto es porque un Suite es un ParentRunner<Runner> en lugar de un Runner como BlockJUnit4ClassRunner que intentaría raspar las reglas en las clases que ejecuta. Para ejecutar sus hijos, le dice al niño Runners que se ejecute. Esos Runner s pueden haber desarrollado sus pruebas mediante la aplicación de reglas en esas clases, pero el corredor Suite no toma ninguna medida especial para aplicar reglas de sí mismo a las pruebas que su hijo Runner s crea.

+0

¿Actualmente no hay forma? Tiene sentido. Me preguntaba sobre la situación en la que una prueba individual fallaría porque suponía que los recursos establecidos por la suite. También tiene sentido que un conjunto simplemente ejecute la prueba unitaria tal como es y que no sea capaz de enganchar * en * su ciclo de vida mientras sigue siendo capaz de engancharse alrededor de él – ArtB

+0

Sin embargo, le diría una sugerencia a @ Yishai. – pholser

0

¿Ha intentado utilizar la "jerarquía de clases de prueba"? A menudo uso la clase de prueba abstracta para compartir la prueba o el accesorio. Por ejemplo, todas mis pruebas DB están inicializando un origen de datos incorporado. Primero creo una clase abstracta "DbTestCase" que maneja la lógica de inicio. Entonces, todas las subclases beneficiarán la prueba y los accesorios.

Sin embargo, a veces me encuentro con un problema cuando mis casos de prueba requieren muchos test/fixture-logic que no puedo almacenar en una sola jerarquía. En este caso, solo la programación de aspectos resuelve el problema. Marcado de prueba/lógica de dispositivo a través de anotaciones/interfaces específicas que cualquier clase requerida puede implementar.

Opcionalmente, puede considerar manejar el "aspecto" utilizando un corredor personalizado que "inyectará" la lógica de la prueba/dispositivo en función de la anotación/interfaz de las clases probadas.

+0

La idea de la jerarquía es sólida, pero los aspectos son como la magia negra o el wasabi: deben usarse con mucha precaución, e incluso solo con moderación. – ArtB

Cuestiones relacionadas