Gancho de ejecución de Before y After Suite en jUnit 4.x

84

Estoy tratando de realizar la configuración y el desmontaje para un conjunto de pruebas de integración, usando jUnit 4.4 para ejecutar las pruebas. El desmontaje debe ejecutarse de manera confiable. Tengo otros problemas con TestNG, por lo que estoy buscando volver a jUnit. ¿Qué ganchos están disponibles para su ejecución antes de que se ejecuten las pruebas y después de que se hayan completado todas las pruebas?

Nota: estamos usando maven 2 para nuestra compilación. Intenté usar las fases pre-& de maven post-integration-test, pero, si falla una prueba, maven se detiene y no se ejecuta post-integration-test, lo cual no ayuda.

sblundy
fuente
1
Para las pruebas de integración, debe usar el complemento maven-failsafe-plugin en lugar de surefire. Esto no saltará post-integration-testsi falla una prueba. Consulte también esta página wiki .
Chris H.
¿puede compartir su implementación final por favor?
vikramvi

Respuestas:

113

Sí, es posible ejecutar de manera confiable métodos de instalación y desmontaje antes y después de cualquier prueba en un conjunto de pruebas. Déjame demostrar en código:

package com.test;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;

@RunWith(Suite.class)
@SuiteClasses({Test1.class, Test2.class})
public class TestSuite {

    @BeforeClass
    public static void setUp() {
        System.out.println("setting up");
    }

    @AfterClass
    public static void tearDown() {
        System.out.println("tearing down");
    }

}

Entonces tu Test1clase se vería así:

package com.test;

import org.junit.Test;


public class Test1 {
    @Test
    public void test1() {
        System.out.println("test1");
    }

}

... y puedes imaginar que se Test2ve similar. Si corriera TestSuite, obtendría:

setting up
test1
test2
tearing down

Por lo tanto, puede ver que la configuración / desmontaje solo se ejecuta antes y después de todas las pruebas, respectivamente.

El truco: esto solo funciona si está ejecutando el conjunto de pruebas y no está ejecutando Test1 y Test2 como pruebas JUnit individuales. Mencionaste que estás usando maven, y al complemento seguro de maven le gusta ejecutar pruebas de forma individual y no como parte de una suite. En este caso, recomendaría crear una superclase que amplíe cada clase de prueba. La superclase contiene los métodos @BeforeClass y @AfterClass anotados. Aunque no es tan limpio como el método anterior, creo que funcionará para usted.

En cuanto al problema con las pruebas fallidas, puede configurar maven.test.error.ignore para que la compilación continúe en las pruebas fallidas. Esto no se recomienda como una práctica continua, pero debería ayudarlo a funcionar hasta que todas sus pruebas pasen. Para obtener más detalles, consulte la documentación segura de maven .

Julie
fuente
2
Esto funcionó perfectamente para mí una vez que entré en el plugin maven-surefire-plugin y creé una lista de inclusiones que apuntaba a la suite que quería ejecutar.
Jherico
2
A partir de JUnit 4.8.2, esto no funciona bien con las pruebas parametrizadas. Los métodos @BeforeClass de la Suite se ejecutarán después del método @ Parameterized.Parameters de la prueba, evitando cualquier dependencia de la configuración de la Suite.
Anm
En respuesta a mí mismo, cuando uso @Theories, la llamada al método @DataPoints se llama después de @BeforeClass de la Suite.
Anm
18
Perdón por la necro, pero agregar BeforeClass / AfterClass a una superclase no funciona como se esperaba, todavía se llaman después de que se completa cada clase de prueba. Esto es para la posteridad.
Subu Sankara Subramanian
3
¿Sigue siendo este un enfoque válido? ¿Cómo puede evitar la necesidad de enumerar la lista de clases de prueba en la anotación SuiteClasses?
Burhan Ali
33

Un colega mío sugirió lo siguiente: puede usar un RunListener personalizado e implementar el método testRunFinished (): http://junit.sourceforge.net/javadoc/org/junit/runner/notification/RunListener.html#testRunFinished(org. junit.runner.Result)

Para registrar RunListener, simplemente configure el complemento surefire de la siguiente manera: http://maven.apache.org/surefire/maven-surefire-plugin/examples/junit.html sección "Uso de oyentes y reporteros personalizados"

Esta configuración también debe ser elegida por el complemento a prueba de fallas. Esta solución es excelente porque no tiene que especificar Suites, clases de prueba de búsqueda o nada de esto; le permite a Maven hacer su magia, esperando que terminen todas las pruebas.

Martin Vysny
fuente
5
+1 ¡Esa es la primera solución utilizable que he visto sin el engorroso mantenimiento de una clase de Suites!
Stefan Haberl
7

Usando anotaciones, puede hacer algo como esto:

import org.junit.*;
import static org.junit.Assert.*;
import java.util.*;

class SomethingUnitTest {
    @BeforeClass
    public static void runBeforeClass()
    {

    }

    @AfterClass
    public static void runAfterClass()
    {  

    }

    @Before  
    public void setUp()
    {

    }

    @After
    public void tearDown()
    {

    }

    @Test
    public void testSomethingOrOther()
    {

    }

}
Jroc
fuente
4
La configuración y el desmontaje deben ejecutarse una vez por ejecución. Esto solo ayudaría si todas las pruebas se realizan en una clase.
sblundy
3

Aquí nosotros

  • actualizado a JUnit 4.5,
  • Escribió anotaciones para etiquetar cada clase o método de prueba que necesitaba un servicio funcional,
  • escribieron controladores para cada anotación que contenían métodos estáticos para implementar la configuración y el desmontaje del servicio,
  • extendió el Runner habitual para ubicar las anotaciones en las pruebas, agregando los métodos de manejo estático en la cadena de ejecución de la prueba en los puntos apropiados.

fuente
2

En cuanto a "Nota: estamos usando maven 2 para nuestra compilación. Intenté usar las fases de prueba previa y posterior a la integración, pero, si una prueba falla, maven se detiene y no ejecuta la prueba posterior a la integración , lo cual no ayuda ".

en su lugar, puede probar el complemento a prueba de fallos, creo que tiene la capacidad de garantizar que se realice la limpieza independientemente de la configuración o el estado de la etapa intermedia

Pantalón Binod
fuente
Sí, el complemento a prueba de fallos le permitirá especificar la configuración y el desmontaje específicos. Aunque no creo que existiera a prueba de fallos en el momento en que se publicó esta pregunta.
Jason Axelson
2

Siempre que todas sus pruebas puedan extender una clase "técnica" y estén en el mismo paquete, puede hacer un pequeño truco:

public class AbstractTest {
  private static int nbTests = listClassesIn(<package>).size();
  private static int curTest = 0;

  @BeforeClass
  public static void incCurTest() { curTest++; }

  @AfterClass
  public static void closeTestSuite() {
      if (curTest == nbTests) { /*cleaning*/ }             
  }
}

public class Test1 extends AbstractTest {
   @Test
   public void check() {}
}
public class Test2 extends AbstractTest {
   @Test
   public void check() {}
}

Tenga en cuenta que esta solución tiene muchos inconvenientes:

  • debe ejecutar todas las pruebas del paquete
  • debe subclasificar una clase "técnica"
  • no puede usar @BeforeClass y @AfterClass dentro de subclases
  • si ejecuta solo una prueba en el paquete, la limpieza no se realiza
  • ...

Para obtener información: listClassesIn () => ¿Cómo se encuentran todas las subclases de una clase determinada en Java?

christophe blin
fuente
1
esto no es cierto por lo que muestran mis propias pruebas. Tengo una superclase que inicia un pez de cristal incrustado antes de la clase y lo cierra después de la clase. Entonces tengo 2 clases que se extienden desde esa superclase. La clase anterior se ejecuta antes de ejecutar las pruebas definidas en cada clase.
Jonathan Morales Vélez
0

Hasta donde yo sé, no hay ningún mecanismo para hacer esto en JUnit, sin embargo, puede intentar subclasificar Suite y anular el método run () con una versión que proporcione ganchos.

usuario15299
fuente
0

Dado que maven-surefire-plugin no ejecuta la clase Suite primero, pero trata la suite y las clases de prueba de la misma manera, podemos configurar el complemento como se muestra a continuación para habilitar solo las clases de la suite y deshabilitar todas las pruebas. Suite ejecutará todas las pruebas.

        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.5</version>
            <configuration>
                <includes>
                    <include>**/*Suite.java</include>
                </includes>
                <excludes>
                    <exclude>**/*Test.java</exclude>
                    <exclude>**/*Tests.java</exclude>
                </excludes>
            </configuration>
        </plugin>
Anurag Sharma
fuente
0

La única forma en que creo que obtendría la funcionalidad que desea sería hacer algo como

import junit.framework.Test;  
import junit.framework.TestResult;  
import junit.framework.TestSuite;  

public class AllTests {  
    public static Test suite() {  
        TestSuite suite = new TestSuite("TestEverything");  
        //$JUnit-BEGIN$  
        suite.addTestSuite(TestOne.class);  
        suite.addTestSuite(TestTwo.class);  
        suite.addTestSuite(TestThree.class);  
        //$JUnit-END$  
     }  

     public static void main(String[] args)  
     {  
        AllTests test = new AllTests();  
        Test testCase = test.suite();  
        TestResult result = new TestResult();  
        setUp();  
        testCase.run(result);  
        tearDown();  
     }  
     public void setUp() {}  
     public void tearDown() {}  
} 

Utilizo algo como esto en eclipse, así que no estoy seguro de qué tan portátil es fuera de ese entorno

Jroc
fuente
3
Este es un ejemplo para JUnit3, y el OP pidió JUnit4, pero en caso de que algunos usuarios de JUnit3 encuentren esta pregunta ... Para JUnit3, sería mejor deshacerse del método main () y tener el método suite () envuelva el TestSuite en una subclase de junit.extensions.TestSetup. Todavía tiene las mismas advertencias que el ejemplo de Julie sobre la ejecución de las clases de prueba individuales en su IDE.
NamshubWriter
0

Si no desea crear una suite y tiene que enumerar todas sus clases de prueba, puede usar la reflexión para encontrar la cantidad de clases de prueba dinámicamente y hacer una cuenta regresiva en una clase base @AfterClass para hacer el tearDown solo una vez:

public class BaseTestClass
{
    private static int testClassToRun = 0;

    // Counting the classes to run so that we can do the tear down only once
    static {
        try {
            Field field = ClassLoader.class.getDeclaredField("classes");
            field.setAccessible(true);

            @SuppressWarnings({ "unchecked", "rawtypes" })
            Vector<Class> classes = (Vector<Class>) field.get(BlockJUnit4ClassRunner.class.getClassLoader());
            for (Class<?> clazz : classes) {
                if (clazz.getName().endsWith("Test")) {
                    testClassToRun++;
                }
            }
        } catch (Exception ignore) {
        }
    }

    // Setup that needs to be done only once
    static {
        // one time set up
    }

    @AfterClass
    public static void baseTearDown() throws Exception
    {
        if (--testClassToRun == 0) {
            // one time clean up
        }
    }
}

Si prefiere usar @BeforeClass en lugar de los bloques estáticos, también puede usar una marca booleana para realizar el recuento de reflejos y la configuración de prueba solo una vez en la primera llamada. Espero que esto ayude a alguien, me tomó una tarde encontrar una manera mejor que enumerar todas las clases en una suite.

Ahora todo lo que necesita hacer es extender esta clase para todas sus clases de prueba. Ya teníamos una clase base para proporcionar algunas cosas comunes para todas nuestras pruebas, por lo que esta fue la mejor solución para nosotros.

La inspiración proviene de esta respuesta SO https://stackoverflow.com/a/37488620/5930242

Si no desea extender esta clase a todas partes, esta última respuesta SO podría hacer lo que desee.

FredBoutin
fuente