Junit antes de clase (no estático)

84

¿Existen mejores prácticas para que Junit ejecute una función una vez en un archivo de prueba, y tampoco debería ser estático?

como @BeforeClassen la función no estática?

Aquí hay una fea solución:

@Before void init(){
    if (init.get() == false){
        init.set(true);
        // do once block
    }
}

Bueno, esto es algo que no quiero hacer y estoy buscando una solución junit integrada.

romano
fuente
Bueno, tengo una jerarquía bastante grande de archivos de prueba y archivos de prueba base, necesito la posibilidad de anular esta acción en las clases de prueba secundarias.
Roman
1
Tuve el mismo problema en el que solo la primera de muchas pruebas parametrizadas debería realizar un inicio de sesión.
dokaspar
5
Tenga en cuenta que la solución "fea", la que funciona con JUnit simple, no tiene en cuenta las pruebas de desgarro.
eskatos

Respuestas:

22

Si no desea configurar inicializadores estáticos para una inicialización única y no es particular sobre el uso de JUnit, eche un vistazo a TestNG. TestNG admite la inicialización no estática de una sola vez con una variedad de opciones de configuración, todas usando anotaciones.

En TestNG, esto sería equivalente a:

@org.testng.annotations.BeforeClass
public void setUpOnce() {
   // One time initialization.
}

Para desmontaje,

@org.testng.annotations.AfterClass
public void tearDownOnce() {
   // One time tear down.
}

Para el equivalente TestNG de JUnit 4 @Beforey @After, puede usar @BeforeMethody @AfterMethodrespectivamente.

Kartik
fuente
41

Una simple declaración if también parece funcionar bastante bien:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:test-context.xml"})
public class myTest {

    public static boolean dbInit = false;

    @Autowired
    DbUtils dbUtils;

    @Before
    public void setUp(){

        if(!dbInit){

            dbUtils.dropTables();
            dbUtils.createTables();
            dbInit = true;

        }
    }

 ...
Upgradingdave
fuente
1
¡Agradable y sencillo! ¿Pero no ve una forma de adaptar esto simplemente para hacer un @AfterClassequivalente no estático que se rompa después de que se hayan ejecutado todas las pruebas?
Steve Chambers
1
Consulte aquí para obtener una actualización de este método que debería funcionar para las clases de prueba que usan herencia.
Steve Chambers
36

Utilizar un constructor vacío es la solución más sencilla. Aún puede anular el constructor en la clase extendida.

Pero no es óptimo con toda la herencia. Es por eso que JUnit 4 usa anotaciones en su lugar.

Otra opción es crear un método auxiliar en una clase factory / util y dejar que ese método haga el trabajo.

Si está usando Spring, debería considerar usar la @TestExecutionListenersanotación. Algo como esta prueba:

@RunWith(SpringJUnit4ClassRunner.class)
@TestExecutionListeners({CustomTestExecutionListener.class, 
     DependencyInjectionTestExecutionListener.class})
@ContextConfiguration("test-config.xml")
public class DemoTest {

Spring AbstractTestExecutionListenercontiene, por ejemplo, este método vacío que puede anular:

public void beforeTestClass(TestContext testContext) throws Exception {
    /* no-op */
}

NOTA: NO pase por alto / pase por alto DependencyInjectionTestExecutionListeneral agregar personalizado TestExecutionListeners. Si lo hace, todos los cables automáticos serán null.

Espen
fuente
+1 Esta técnica resolvió mi problema al querer usar DbUnit y solo cargar el conjunto de datos una vez por clase
Brad
+1 Esto es perfecto ... para personas que no están atadas a una versión antigua de Spring. :(
Mike Miller
1
¿Se beforeTestClass()llamará antes o después de la inicialización del contexto?
Atenúa el
@Dims after context initialized
Anand Rockzz
7

Utilice fácilmente @BeforeAllMethods/ @AfterAllMethodsanotaciones para ejecutar un método dentro del contexto de la instancia (no estático), donde estarán disponibles todos los valores inyectados.

Hay una biblioteca de pruebas especial para esto:

https://mvnrepository.com/artifact/org.bitbucket.radistao.test/before-after-spring-test-runner/0.1.0

https://bitbucket.org/radistao/before-after-spring-test-runner/

La única limitación: funciona solo para las pruebas de Spring .

(Soy el desarrollador de esta biblioteca de pruebas)

radistao
fuente
0

Nunca lo intenté, pero tal vez puedas crear un constructor sin argumentos y llamar a tu función desde allí.

romano
fuente
Esto funcionaría, el problema es que necesito la posibilidad de anular esta acción en las clases que extienden esta clase de prueba base
Roman
@Roman: oh, ahora veo. Agregue esto a su publicación, este comentario aclara mucho las cosas.
Roman
Se llamará al constructor tantas veces como haya casos de prueba. Para cada método de prueba, se creará un nuevo objeto de clase de prueba. Entonces, usar el constructor no es una solución aquí
manikanta
Además, esto no funcionará con la inyección de dependencia que se basa en el objeto ya construido.
Mike Miller
0

El artículo analiza 2 soluciones muy buenas para este problema:

  1. junit "limpio" con Runner personalizado (usando la interfaz, pero puede ampliarlo con una anotación personalizada, por ejemplo, @BeforeInstance)
  2. Oyentes de ejecución de primavera como lo mencionó Espen antes.
kedzi
fuente
0

ACTUALIZACIÓN: Consulte el comentario de Cherry sobre por qué la sugerencia a continuación es defectuosa. (Mantengo la respuesta aquí en lugar de eliminarla, ya que el comentario puede proporcionar información útil a otros sobre por qué esto no funciona).


Otra opción que vale la pena considerar si se usa la inyección de dependencia (por ejemplo, Spring) es @PostConstruct. Esto garantizará que la inyección de dependencias esté completa, lo que no sería el caso en un constructor:

@PostConstruct
public void init() {
    // One-time initialization...
}

Steve Cámaras
fuente
7
Muy mala solución en caso de pruebas Junit. Junit crea una instancia de clase de prueba cada vez que ejecuta un método de prueba. Entonces, si hay 6 métodos de prueba en la clase, ¡un constructor de clase @Beforey los @Aftermétodos se llamarán 6 veces! Entonces en este contexto se @PostConstructcomporta como una @Beforeanotación. Simplemente puede probarlo: simplemente coloque 2 métodos de prueba en la clase de prueba, agregue @PostConstruct public void init() {System.out.println("started");}y vea en los registros cuántas veces se imprime.
Cherry
Para obtener información, me encontré con la documentación de JUnit que confirma lo que se describe en el comentario anterior sobre la creación de una instancia de JUnit para cada @Testejecución: "Para ejecutar el método, JUnit primero construye una instancia nueva de la clase y luego invoca el método anotado".
Steve Chambers
-2

Solo usa @BeforeClass:

@BeforeClass
public static void init() {
}

No tiene sentido initque no sea estático porque cada prueba se ejecuta en una instancia separada. La instancia que initse ejecuta no coincidirá con la instancia de ninguna prueba.

La única razón por la que puede querer que no sea estático es anularlo en subclases, pero también puede hacerlo con métodos estáticos. Simplemente use el mismo nombre, y solo initse llamará al método de subclase .

fgb
fuente
2
Toda esta pregunta trata sobre la posibilidad de hacerlo de una manera no estática, que es necesaria si necesita algunas variables de instancia en la clase.
Simon Forsberg
@SimonForsberg Sí, y estoy diciendo que la pregunta es un problema XY. El operador dijo que el problema estaba anulando el comportamiento en las clases para niños. Si el ejemplo necesita variables de instancia, podría sugerir algo más.
fgb
Vea este comentario: stackoverflow.com/questions/2825615/…
Simon Forsberg
@SimonForsberg Ese es el comentario del que estaba hablando. ¿Qué pasa con eso?
fgb