2010-01-25 11 views
65

Tengo una clase base abstracta, que utilizo como base para las pruebas de mi unidad (TestNG 5.10). En esta clase, inicializo todo el entorno para mis pruebas, estableciendo mapeos de bases de datos, etc. Esta clase abstracta tiene un método con una anotación @BeforeClass que hace la inicialización.@BeforeClass and inheritance - orden de ejecución

A continuación, extiendo esa clase con clases específicas en las que tengo @Test métodos y también @BeforeClass métodos. Estos métodos hacen una inicialización del entorno específica de la clase (por ejemplo, poner algunos registros en la base de datos).

¿Cómo puedo hacer cumplir un orden específico de los métodos anotados @BeforeClass? Necesito que los de la clase base abstracta se ejecuten antes que los de la clase extendida.

Ejemplo: Para

abstract class A { 
    @BeforeClass 
    doInitialization() {...} 
} 

class B extends A { 
    @BeforeClass 
    doSpecificInitialization() {...} 

    @Test 
    doTests() {...} 
} 

esperado:

A.doInitialization 
B.doSpecificInitialization 
B.doTests 

orden real:

B.doSpecificInitialization // <- crashes, as the base init is missing 
(A.doInitialization  // <---not executed 
B.doTests)    // <-/ 

Respuesta

35

no pone el @BeforeClass en la clase abstract. Llamarlo desde cada subclase.

abstract class A { 
    void doInitialization() {} 
} 

class B extends A { 
    @BeforeClass 
    void doSpecificInitialization() { 
     super.doInitialization(); 
    } 

    @Test 
    void doTests() {} 
} 

parece que TestNG tiene @BeforeClass(dependsOnMethods={"doInitialization"}) - darle una oportunidad.

+5

eso es básicamente lo que quería evitar: no hay necesidad de llamar explícitamente métodos de la superclase (resumen). Especialmente porque también tengo clases, que heredan de A pero que no tienen un método propio de @BeforeClass. Tendría que insertar uno solo para ese propósito. –

+5

La solución 'dependsOnMethods' hizo el truco. Aunque preferiría un enfoque de "superclase primero" ... –

+1

Para usar "dependsOnMethod" no se debe anotar "doInitialization" con "@Test"? Eso es un problema, ya que técnicamente no es una prueba en sí misma ... – N3da

76

edición: respuesta a continuación es para JUnit, pero voy a dejarlo aquí de todos modos, ya que podría ser útil.

De acuerdo con JUnit api: "Los métodos @BeforeClass de superclases se ejecutarán antes que aquellos de la clase actual."

He probado esto y parece funcionar para mí.

Sin embargo, como se menciona a continuación @Odys, por JUnit es necesario tener los dos métodos denominados de forma diferente aunque como hacer lo contrario dará lugar a sólo el método que se está ejecutando subclase porque será ensombrecido el padre.

+44

Aunque la pregunta original era para TestNG, llegué aquí después de buscar en Google JUnit y su respuesta ayudó, ¡gracias! – teabot

+5

para JUnit ** usted necesita ** tener los dos métodos nombrados de manera diferente, ya que al hacerlo, de lo contrario, solo se ejecutará el método de la subclase porque el padre se sombreará. – Odys

+1

@Odys, muchas gracias por mencionar esto. Estaba luchando para descubrir por qué se estaba ejecutando el método "setup" en mi subclase, mientras que el de su superclase no. ¡Me has ahorrado un montón de molestias! –

6

Acabo de probar su ejemplo con 5.11 y obtengo el @BeforeClass de la clase base invocada primero.

¿Puedes publicar tu archivo testng.xml? Tal vez estés especificando A y B allí, mientras que solo B es necesario.

Siéntase libre de seguir en la lista de correo de usuarios de testng y podemos analizar su problema de cerca.

- Cedric

+1

No .xml para testng definido (explícitamente), se ejecuta desde Eclipse y Maven. –

+0

¿Cómo lo está ejecutando desde Eclipse exactamente? Al hacer clic derecho en la clase B? –

2

¿Qué hay de tener su método @BeforeClass llamar un método vacío specificBeforeClass() que pueden o no pueden ser sobrescritos por subclases de esta manera:

public class AbstractTestClass { 
    @BeforeClass 
    public void generalBeforeClass() { 
    // do stuff 
    specificBeforeClass(); 
    } 

    protected void specificBeforeClass() {} 
} 

public class SpecificTest { 
    @Override 
    protected void specificBeforeClass() { 
    // Do specific stuff 
    } 

    // Tests 
} 
+2

BeforeClass debe ser estático, por lo que no puede hacer esto con junit – madx

6

añadí a public la clase abstracta y TestNG (6.0.1) ejecutaron doInitialization() antes de doTests.TestNG no ejecuta doInitialization() si quito public de la clase A.

public abstract class A { 
@BeforeClass 
doInitialization() {...} 
} 

class B extends A {  
@Test 
doTests() {...} 
} 
+1

Eso es cierto, pero irrelevante. Esto no funciona cuando la clase 'B' * también * tiene un método' @ BeforeClass'-anotado, como en el caso del OP. – jpaugh

+1

Hice lo mismo. Parece que el orden de herencia se pasa por alto si el método base es privado. ¡Gracias! – Manu

2

Cuando corro de: JUnitCore.runClasses (TestClass.class); se ejecutará el padre correctamente, antes de que el niño (No es necesario super.SetUpBeforeClass();) Si se ejecuta desde Eclipse: Por alguna razón no se ejecuta la clase base. El trabajo alrededor: Llamar explícitamente a la clase base: (BaseTest.setUpBeforeClass();) Es posible que desee tener un indicador en la clase base en caso de que lo ejecute desde una aplicación, para determinar si ya está configurado o no. Por lo tanto, solo se ejecuta una vez si lo ejecuta a través de ambos métodos posibles (por ejemplo, desde eclipse para pruebas personales, y a través de ANT para una versión de compilación).

Esto parece ser un error con Eclipse, o al menos los resultados inesperados ..

-1

En mi caso (JUnit) tengo los mismos métodos llamados de configuración() en la clase base y la clase derivada. En este caso solo se llama al método de la clase derivada, y lo hago llamar al método de la clase base.

0

¿Por qué no intentas crear un método abstracto doSpecialInit() en tu superclase, llamado desde tu método anotado BeforeClass en superclase?

Entonces los desarrolladores que hereden su clase se ven obligados a implementar este método.

+0

Para ser honesto, incluso la lógica puede haber cambiado en los últimos 3 1/2 años desde que hice esta pregunta ... ;-) Así que sí, tal vez esto fue una idea, tal vez no funcionó - Honestamente, no lo recuerdo. –

3

Acabo de pasar por esto y encontré una forma más de lograr esto. Solo use alwaysRun en @BeforeClass o @BeforeMethod en la clase abstracta, funciona como era de esperar.

public class AbstractTestClass { 
    @BeforeClass(alwaysRun = true) 
    public void generalBeforeClass() { 
     // do stuff 
     specificBeforeClass(); 
    } 
} 
1

dependsOnMethod se puede utilizar.

p. Ej. en el caso de la primavera (AbstractTestNGSpringContextTests)

@BeforeClass(alwaysRun = true, dependsOnMethods = "springTestContextPrepareTestInstance") 
-2

Una manera mejor y más limpio para lograr esto usando herencia puede ser de la siguiente manera -

abstract class A { 

    @BeforeClass 
    void doInitialization() {} 
} 

class B extends A { 

    @Override 
    @BeforeClass 
    void doInitialization() { 
     super.doInitialization(); 
    } 

    @Test 
    void doTests() {} 
} 
+0

Si necesita que se ejecute primero el método en la clase Parent, solo debe nombrar el método en la clase Child de forma diferente a la del Parent (porque si tienen la misma firma, entonces el polimorfismo viene en acción). Creo que es una manera más limpia. –

2

Para JUnit: Como @fortega ha mencionado: Según a la API de JUnit: "Los métodos @BeforeClass de las superclases se ejecutarán antes que los de la clase actual".

Pero tenga cuidado para no nombrar ambos métodos con el mismo nombre. Dado que en este caso el padre padre ocultará el método principal. Source.

0

Aquí hay otra solución fácil.

Mi situación particular es que necesito inyectar servicios simulados desde "BeforeClass" en la subclase antes de que se ejecute "BeforeClass" en la superclase.

Para hacer esto, simplemente use un @ClassRule en la subclase.

Por ejemplo:

@ClassRule 
public static ExternalResource mocksInjector = new ExternalResource() { 
    @Override 
    protected void before() { 
     // inject my mock services here 
     // Note: this is executed before the parent class @BeforeClass 
    } 
}; 

espero que esto ayude. Esto puede ejecutar de manera efectiva la configuración estática en orden "inverso".

1

Revise su declaración de importación. Debe ser

import org.testng.annotations.BeforeClass;

no

import org.junit.BeforeClass;