¿Cómo definir una regla de método JUnit en un banco de pruebas?

Tengo una clase que es un conjunto JUnit de clases de prueba JUnit. Me gustaría definir una regla en el paquete para hacer algo en la base de datos antes y después de ejecutar cada prueba unitaria si hay una cierta anotación presente en ese método de prueba.

He podido crear una @ClassRule en las suites y probar las clases que harán esto antes de cada clase (lo cual no es lo suficientemente bueno) y he podido definir las reglas de prueba con las clases de prueba en sí, 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.

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. Usando las siguientes clases Suite y 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 anotado test1() con @Deprecated . Desea hacer algo diferente cuando tiene la anotación @Deprecated en la prueba, por lo que debemos extender Suite para usar un Runner personalizado:

 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 getRunners(Class[] classes) throws InitializationError { List runners = new LinkedList(); for (Class klazz : classes) { runners.add(new MyRunner(klazz)); } return runners; } } 

JUnit crea un Runner para cada prueba que ejecutará. Normalmente, Suite simplemente crearía el BlockJUnit4ClassRunner predeterminado, todo lo que estamos haciendo aquí es anular el constructor para el Suite 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); } } } 

La mayor parte de esto se copia de BlockJUnit4ClassRunner . El bit que he agregado es:

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

donde probamos la existencia de la anotación @Deprecated en el método, y hacemos algo si está allí. El rest queda como ejercicio para el lector. Cuando ejecuto la Suite anterior, obtengo 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 múltiples constructores según cómo se invoque. 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.

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 RunListener a una Suite aquí .

Por sí solo, agregar una regla a la clase anotada con @RunWith(Suite.class) no servirá. Creo que esto se debe a que una Suite es un ParentRunner lugar de un Runner como BlockJUnit4ClassRunner que intentaría raspar las reglas en las clases que ejecuta. Para ejecutar sus hijos, le dice a los Runners secundarios que se ejecuten. Esos Runner pueden haber acumulado sus pruebas aplicando reglas en esas clases, pero el corredor de la Suite no toma ninguna acción especial para aplicar reglas de sí mismo a las pruebas que su hijo construye.

¿Has intentado usar “jerarquía de clase 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 encuentro 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 progtwigció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 dependiendo de la anotación / interfaz de las clases probadas.

Puede agrupar las pruebas con TestNG. Y puede configurar TestNG para ejecutar alguna lógica @BeforeGroup y @AfterGroup .