2010-01-25 21 views
65

Ho una classe base astratta, che uso come base per i miei test di unità (TestNG 5.10). In questa classe, inizializzo l'intero ambiente per i miei test, impostando i mapping dei database, ecc. Questa classe astratta ha un metodo con un'annotazione @BeforeClass che esegue l'inizializzazione.@BeforeClass ed ereditarietà - ordine di esecuzione

Successivamente, estendo tale classe con classi specifiche in cui dispongo di metodi @Test e anche di metodi @BeforeClass. Questi metodi eseguono l'inizializzazione specifica della classe dell'ambiente (ad esempio, inseriscono alcuni record nel database).

Come è possibile applicare un ordine specifico dei metodi annotati @BeforeClass? Ho bisogno che quelli della classe base astratta siano eseguiti prima di quelli della classe che si estende.

Esempio: ordine

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

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

    @Test 
    doTests() {...} 
} 

atteso:

A.doInitialization 
B.doSpecificInitialization 
B.doTests 

ordine attuale:

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

risposta

35

non mettere il @BeforeClass sulla classe abstract. Chiamalo da ogni sottoclasse.

abstract class A { 
    void doInitialization() {} 
} 

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

    @Test 
    void doTests() {} 
} 

Sembra come TestNG ha @BeforeClass(dependsOnMethods={"doInitialization"}) - fare un tentativo.

+5

Questo è fondamentalmente ciò che volevo evitare: non c'è bisogno di chiamare esplicitamente metodi della super (astratta) di classe. Soprattutto perché ho anche classi, che ereditano da A ma non hanno un proprio metodo @BeforeClass. Dovrei inserire uno solo per quello scopo. –

+5

La soluzione alternativa 'dependsOnMethods' ha funzionato. Anche se preferirei un approccio "superclasse first" ... –

+1

Per usare "dependsOnMethod" non si dovrebbe "eseguire l'inizializzazione" con "@Test"? Questo è un problema dal momento che tecnicamente non è un test da solo ... – N3da

76

edit: risposta sotto è per JUnit, ma mi lascerà qui in ogni caso, perché potrebbe essere utile.

Secondo lo JUnit api: "I metodi di superclassi @BeforeClass verranno eseguiti prima di quelli della classe corrente."

Ho provato questo, e sembra funzionare per me.

Tuttavia, come @Odys cita di seguito, per JUnit è necessario avere le due metodi chiamati in modo diverso anche se, come fare altrimenti comporterà solo il metodo della sottoclasse in esecuzione perché il genitore sarà oscurato.

+44

Anche se la domanda originale era per TestNG, sono arrivato qui dopo aver cercato su google per JUnit e la tua risposta mi ha aiutato - grazie! – teabot

+5

per JUnit ** è necessario ** che i due metodi vengano denominati in modo diverso, poiché altrimenti il ​​risultato sarà solo l'esecuzione del metodo sottoclasse perché il genitore verrà ombreggiato. – Odys

+1

@Odys, grazie mille per aver menzionato questo. Stavo cercando di capire perché il metodo "setup" nella mia sottoclasse era in esecuzione mentre quello nella sua superclasse non lo era. Mi hai appena risparmiato un sacco di esasperazione! –

6

Ho appena provato il tuo esempio con 5.11 e ottengo il @BeforeClass della classe base invocato per primo.

Puoi pubblicare il tuo file testng.xml? Forse stai specificando sia A che B, mentre solo B è necessario.

Sentiti libero di seguire la mailing-list degli utenti testng e possiamo dare un'occhiata più da vicino al tuo problema.

- Cedric

+1

Nessun .xml per testng definito (esplicitamente), è eseguito da Eclipse e Maven. –

+0

Come funziona esattamente da Eclipse? Fare clic destro sulla classe B? –

2

ne dite di avere il tuo metodo di @BeforeClass chiamare un metodo vuoto specificBeforeClass(), che può o non può essere sovrascritto da classi secondarie in questo modo:

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 deve essere statico, quindi non è possibile farlo con junit – madx

6

ho aggiunto public a la classe astratta e TestNG (6.0.1) hanno eseguito la funzione doInitialization() prima di doTests.TestNG non esegue doInitialization() se tolgo public dalla classe A.

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

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

Questo è vero, ma non pertinente. Questo non funziona quando la classe 'B' * ha * anche un metodo annotato' @ BeforeClass', come nel caso dell'OP. – jpaugh

+1

Ho fatto lo stesso. Sembra che l'ordine di ereditarietà sia mancato se il metodo di base è privato. Grazie! – Manu

2

Quando eseguo da: JUnitCore.runClasses (TestClass.class); si eseguirà il genitore correttamente, prima che il bambino (Non è necessario super.SetUpBeforeClass();) Se lo si esegue da Eclipse: Per qualche ragione non riesce a eseguire la classe di base. Il lavoro in giro: Chiamare la classe base in modo esplicito: (BaseTest.setUpBeforeClass();) Si consiglia di avere un flag nella classe base nel caso lo si esegua da un'applicazione, per determinare se è già impostato o no. Quindi viene eseguito una volta sola se lo si esegue tramite entrambi i metodi possibili (ad esempio da eclipse per i test personali e tramite ANT per una versione di build).

Questo sembra essere un bug con Eclipse, o risultati almeno imprevisti ..

-1

Nel mio caso (JUnit) ho gli stessi metodi chiamati setup() nella classe base e la classe derivata. In questo caso solo viene chiamato il metodo della classe derivata e io ho chiamato il metodo della classe base.

0

Perché non provi a creare un metodo astratto doSpecialInit() nella tua super classe, chiamata dal metodo annotato BeforeClass nella superclasse.

Quindi gli sviluppatori che ereditano la tua classe sono costretti a implementare questo metodo.

+0

Per essere onesti, anche la logica potrebbe essere cambiata negli ultimi 3 anni e mezzo da quando ho fatto questa domanda ... ;-) Quindi sì, forse questa era un'idea, forse non ha funzionato - onestamente non lo faccio Ricordo –

3

Ho appena passato questo e ho trovato un altro modo per raggiungere questo obiettivo. Basta usare alwaysRun su @BeforeClass o @BeforeMethod nella classe astratta, funziona come ci si aspetterebbe.

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

dependsOnMethod può essere utilizzato.

ad es. in caso di molla (AbstractTestNGSpringContextTests)

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

Un modo migliore e più pulito per ottenere ciò utilizzando ereditarietà può essere come segue -

abstract class A { 

    @BeforeClass 
    void doInitialization() {} 
} 

class B extends A { 

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

    @Test 
    void doTests() {} 
} 
+0

Se è necessario eseguire il metodo nella classe Parent per essere eseguito per primo, è sufficiente denominare il metodo nella classe Child in modo diverso da quello del genitore (poiché se hanno la stessa firma, il polimorfismo viene attivato). Credo che sia un modo più pulito. –

2

Per JUnit: Come @fortega ha accennato: Secondo to the JUnit api: "I metodi @BeforeClass delle superclassi verranno eseguiti prima di quelli della classe corrente."

Ma attenzione con il non chiamare entrambi i metodi con lo stesso nome. Poiché in questo caso il metodo genitore verrà nascosto dal genitore figlio. Source.

0

C'è un'altra soluzione facile qui.

La mia situazione particolare è che ho bisogno di iniettare servizi di simulazione da "BeforeClass" nella sottoclasse prima dell'esecuzione di "BeforeClass" nella superclasse.

Per fare ciò, utilizzare semplicemente un @ClassRule nella sottoclasse.

Ad esempio:

@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 
    } 
}; 

Spero che questo aiuta. Questo può effettivamente eseguire l'installazione statica in ordine "inverso".

1

Controlla la tua dichiarazione di importazione. Dovrebbe essere

import org.testng.annotations.BeforeClass;

non

import org.junit.BeforeClass;