@BeforeClass ed ereditarietà - ordine di esecuzione


90

Ho una classe base astratta, che uso come base per i miei unit test (TestNG 5.10). In questa classe, inizializzo l'intero ambiente per i miei test, configurando i mapping del database, ecc. Questa classe astratta ha un metodo con @BeforeClassun'annotazione che esegue l'inizializzazione.

Successivamente, estendo quella classe con classi specifiche in cui ho @Testmetodi e anche @BeforeClassmetodi. Questi metodi eseguono l'inizializzazione dell'ambiente specifica della classe (ad esempio, inseriscono alcuni record nel database).

Come posso applicare un ordine specifico dei @BeforeClassmetodi annotati? Ho bisogno che quelli della classe base astratta vengano eseguiti prima di quelli della classe extending.

Esempio:

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

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

    @Test
    doTests() {...}
}

Ordine previsto:

A.doInitialization
B.doSpecificInitialization
B.doTests

Ordine effettivo:

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

Risposte:


50

Non mettere @BeforeClassin abstractclasse. Chiamalo da ogni sottoclasse.

abstract class A {
    void doInitialization() {}
}

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

    @Test
    void doTests() {}
}

Sembra che TestNG abbia @BeforeClass(dependsOnMethods={"doInitialization"})- provalo.


9
Questo è fondamentalmente ciò che volevo evitare: non è necessario chiamare esplicitamente i metodi della classe super (astratta). Soprattutto perché ho anche classi, che ereditano da A ma non hanno un proprio metodo @BeforeClass. Dovrei inserirne uno solo per quello scopo.
Dominik Sandjaja

5
La dependsOnMethodssoluzione ha funzionato. Anche se preferirei un approccio "prima da superclasse" ...
Dominik Sandjaja

1
Per usare "dipendeOnMethod" non dovrebbe "doInitialization" essere annotato con "@Test"? Questo è un problema poiché tecnicamente non è un test di per sé ...
N3da

@BeforeClass dovrebbe annotare un metodo statico
Fabrizio Stellato

108

modifica: la risposta di seguito è per JUnit , ma la lascerò comunque qui, perché potrebbe essere utile.

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

L'ho provato e sembra funzionare per me.

Tuttavia, come menzionato di seguito da @Odys, per JUnit è necessario che i due metodi siano denominati in modo diverso, anche se in caso contrario verrà eseguito solo il metodo della sottoclasse perché il genitore sarà ombreggiato.


56
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

9
per JUnit è necessario che i due metodi siano denominati in modo diverso, anche se altrimenti risulterà in esecuzione solo del metodo della sottoclasse perché il genitore sarà ombreggiato.
Odys il

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

Hai reso la mia giornata. Grazie!
raiks

7

Ho aggiunto publicalla classe abstract e TestNG (6.0.1) ha eseguito prima doInitialization () doTests. TestNG non viene eseguito doInitialization()se rimuovo publicdalla classe A.

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

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

1
Questo è vero, ma irrilevante. Questo non funziona quando la classe ha B anche un @BeforeClassmetodo -annotated, come nel caso dell'OP.
jpaugh

1
Ho fatto lo stesso. Sembra che l'ordine di ereditarietà venga perso se il metodo di base è privato. Grazie!
Manu

6

Ho appena provato il tuo esempio con 5.11 e ottengo la @BeforeClass della classe base invocata per prima.

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

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

- Cedric


2
Nessun .xml per testng definito (esplicitamente), viene eseguito da Eclipse e Maven.
Dominik Sandjaja

Come lo stai eseguendo esattamente da Eclipse? Fare clic con il tasto destro sulla classe B?
Cedric Beust

4

Ho appena passato questo e ho trovato un altro modo per ottenerlo. Basta usare alwaysRunsu @BeforeClasso @BeforeMethodnella classe astratta, funziona come ti aspetteresti.

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

2

Quando eseguo da: JUnitCore.runClasses (TestClass.class); Eseguirà correttamente il genitore, prima del figlio (non hai bisogno di super.SetUpBeforeClass (); ) Se lo esegui da Eclipse: Per qualche motivo non riesce a eseguire la classe base. Soluzione : chiamare la classe di base in modo esplicito: ( BaseTest.setUpBeforeClass (); ) Potresti voler avere un flag nella classe di base nel caso in cui la esegui da un'applicazione, per determinare se è già impostata o meno. Quindi viene eseguito solo una volta se lo si esegue tramite entrambi i metodi possibili (ad esempio da eclipse per test personali e tramite ANT per una versione build).

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


2

Per JUnit : Come ha menzionato @fortega: Secondo l'API JUnit: "I metodi @BeforeClass delle superclassi verranno eseguiti prima di quelli della classe corrente."

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


1

Che ne dici di avere il tuo metodo @BeforeClass chiamare un metodo specificBeforeClass () vuoto che può o non può essere sovrascritto da sottoclassi 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
}

3
BeforeClass deve essere statico, quindi non puoi farlo con junit
madx

1

dependsOnMethod può essere utilizzato.

ad es. in caso di primavera ( AbstractTestNGSpringContextTests)

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

1

Controlla la tua dichiarazione di importazione. Dovrebbe essere

import org.testng.annotations.BeforeClass;

non

import org.junit.BeforeClass;


1

Questo funziona per me -

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

class B extends A {
    @Override
    @BeforeClass
    doInitialization() { 

       //do class specific init

    }   

    @Test
    doTests() {...}
}

1
Aggiungi una breve spiegazione di ciò che fa questo codice e risponde alla domanda originale
Cray

0

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

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


Ad essere onesti, anche la logica potrebbe essere cambiata negli ultimi 3 anni e mezzo da quando ho posto questa domanda ... ;-) Quindi sì, forse questa era un'idea, forse non ha funzionato - Sinceramente no ricorda.
Dominik Sandjaja

0

C'è un'altra semplice soluzione qui.

La mia situazione particolare è che ho bisogno di iniettare servizi fittizi da "BeforeClass" nella sottoclasse prima che "BeforeClass" nella superclasse venga eseguito.

Per fare ciò, usa semplicemente a @ClassRulenella sottoclasse.

Per 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 aiuti. Questo può eseguire efficacemente la configurazione statica in ordine "inverso".


0

Oggi ho affrontato un problema simile, l'unica differenza era che una classe Base non era astratta

Ecco il mio caso

public class A {
    @BeforeClass
    private void doInitialization() {...}
}

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

    @Test
    public void doTests() {...}
}

Si è verificato che un @BeforeClassmetodo della classe A non è mai stato eseguito.

  • A.doInitialization () -> QUESTO NON È STATO MAI ESEGUITO silenziosamente
  • B.doSpecificInitialization ()
  • B.doTests ()

Giocando con i modificatori della privacy ho scoperto che TestNG non eseguirà un @BeforeClassmetodo annotato dalla classe ereditata se un metodo non è visibile da un ereditatore di classe

Quindi questo funzionerà:

public class A {
    @BeforeClass
    private void doInitialization() {...}
}

public class B extends A {
    @BeforeClass
    //Here a privacy modifier matters -> please make sure your method is public or protected so it will be visible for ancestors
    protected void doSpecificInitialization() {...}

    @Test
    public void doTests() {...}
}

Di conseguenza accade quanto segue:

  • A.doInitialization ()
  • B.doSpecificInitialization ()
  • B.doTests ()

-1

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


-2

Un modo migliore e più pulito per ottenere questo risultato utilizzando l'ereditarietà potrebbe essere il seguente:

abstract class A {

    @BeforeClass
    void doInitialization() {}
}

class B extends A {

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

    @Test
    void doTests() {}
}

1
Se hai bisogno che il metodo nella classe Parent venga eseguito per primo, devi solo nominare il metodo nella classe Child in modo diverso da quello del Parent (perché se hanno la stessa firma allora il polimorfismo entra in azione). Credo che sia un modo più pulito.
Anatolii Stepaniuk
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.