Quale ordine vengono chiamati Junit @ Before / @ After?


133

Ho una suite di test di integrazione. Ho una IntegrationTestBaseclasse per estendere tutti i miei test. Questa classe di base ha un metodo @Before( public void setUp()) e @After( public void tearDown()) per stabilire connessioni API e DB. Quello che ho fatto è semplicemente ignorare quei due metodi in ogni testcase e chiamare super.setUp()e super.tearDown(). Tuttavia, ciò può causare problemi se qualcuno dimentica di chiamare il super o li mette nel posto sbagliato e viene generata un'eccezione e si dimenticano di chiamare super nel finalmente o qualcosa del genere.

Quello che voglio fare è creare i metodi setUpe tearDownsulla classe base finale quindi aggiungere i nostri metodi @Beforee annotati @After. Effettuando alcuni test iniziali sembra chiamare sempre in questo ordine:

Base @Before
Test @Before
Test
Test @After
Base @After

ma sono solo un po 'preoccupato che l'ordine non sia garantito e che possa causare problemi. Mi sono guardato intorno e non ho visto nulla sull'argomento. Qualcuno sa se posso farlo e non ho problemi?

Codice:

public class IntegrationTestBase {

    @Before
    public final void setUp() { *always called 1st?* }

    @After
    public final void tearDown() { *always called last?* }
}


public class MyTest extends IntegrationTestBase {

    @Before
    public final void before() { *always called 2nd?* }

    @Test
    public void test() { *always called 3rd?* }

    @After
    public final void after() { *always called 4th?* }
}

1
È la MyTestmanca una extends?
aioobe,

@aioobe: non più :)
Joel

Risposte:


135

Sì, questo comportamento è garantito:

@Before:

I @Beforemetodi delle superclassi verranno eseguiti prima di quelli della classe corrente, a meno che non vengano sovrascritti nella classe corrente. Nessun altro ordine è definito.

@After:

I @Aftermetodi dichiarati nelle superclassi verranno eseguiti dopo quelli della classe corrente, a meno che non vengano sovrascritti nella classe corrente.


15
Per essere chiari, l'ordine di esecuzione di tutti i @Beforemetodi non è garantito. Se ci sono 10 @Beforemetodi, ognuno di essi può essere eseguito in qualsiasi ordine; appena prima di qualsiasi altro metodo.
Swati,

5
Quindi, invece di citare la documentazione in qualche modo ambigua, puoi per favore spiegarla con parole tue? Sono @Beforee@After metodi eseguite prima di ogni altro metodo di classe (una volta al metodo), o solo prima e dopo l'intera suite di metodi di classe (una volta per ogni classe)?
BT,

5
Vedi l'importante pescato dichiarato da John Q Citizen: "questo vale solo se ogni metodo contrassegnato con @Before ha un nome univoco nella gerarchia di classi" Molto importante da ricordare!
Bruno Bossola,

Ho avuto un conflitto di nomi usando lo stesso nome di metodo su un metodo @Before (d) in una classe e un altro metodo nella sua super classe, su junit-4.12.
Stephane

Questa regola si applica anche ai metodi @BeforeExample di ConcordionRunner?
Adrian Pronk,

51

Un potenziale gotcha che mi ha morso prima:

Mi piace avere al massimo un @Beforemetodo in ogni classe di test, perché l'ordine di esecuzione dei @Beforemetodi definiti all'interno di una classe non è garantito. In genere, chiamerò un tale metodo setUpTest().

Tuttavia, sebbene @Beforesia documentato come The @Before methods of superclasses will be run before those of the current class. No other ordering is defined., questo vale solo se ogni metodo contrassegnato con @Beforeha un nome univoco nella gerarchia di classi.

Ad esempio, ho avuto il seguente:

public class AbstractFooTest {
  @Before
  public void setUpTest() { 
     ... 
  }
}

public void FooTest extends AbstractFooTest {
  @Before
  public void setUpTest() { 
    ...
  }
}

Mi aspettavo AbstractFooTest.setUpTest()di correre prima FooTest.setUpTest(), ma è FooTest.setupTest()stato eseguito solo . AbstractFooTest.setUpTest()non è stato chiamato affatto.

Il codice deve essere modificato come segue per funzionare:

public void FooTest extends AbstractFooTest {
  @Before
  public void setUpTest() {
    super.setUpTest();
    ...
  }
}

Perché non cambiare semplicemente il nome del metodo @Before nella classe base? Questo ti eviterà di dover chiamare il super in tutti i bambini ... comunque buona cattura con lo stesso problema di rilascio
Lawrence Tierney

24
Solo un'osservazione per rendere le cose più sicure: per evitare gli scontri con i nomi puoi rendere @Before/ i @Aftermetodi nella classe base final, quindi il compilatore si lamenterà se (accidentalmente) provi a sovrascriverli nella sottoclasse.
Stefan Winkler,

4
Il metodo genitore con lo stesso nome non in esecuzione non suona come un comportamento JUnit. Sembra come funziona l'over-riding di base in OOP. Fondamentalmente il metodo genitore non esiste in fase di esecuzione. Il bambino lo sostituisce a tutti gli effetti. Ecco come funziona Java.
Brandon,

1
Un altro problema è che le classi parent devono essere pubbliche, altrimenti i loro @Beforemetodi contrassegnati verranno ignorati se anche una sottoclasse ha un @Beforemetodo.
Rusins,

21

Penso che sulla base della documentazione della @Beforee @Aftergiusta conclusione sia dare ai metodi nomi univoci. Uso il seguente modello nei miei test:

public abstract class AbstractBaseTest {

  @Before
  public final void baseSetUp() { // or any other meaningful name
    System.out.println("AbstractBaseTest.setUp");
  }

  @After
  public final void baseTearDown() { // or any other meaningful name
    System.out.println("AbstractBaseTest.tearDown");
  }
}

e

public class Test extends AbstractBaseTest {

  @Before
  public void setUp() {
    System.out.println("Test.setUp");
  }

  @After
  public void tearDown() {
    System.out.println("Test.tearDown");
  }

  @Test
  public void test1() throws Exception {
    System.out.println("test1");
  }

  @Test
  public void test2() throws Exception {
    System.out.println("test2");
  }
}

dare di conseguenza

AbstractBaseTest.setUp
Test.setUp
test1
Test.tearDown
AbstractBaseTest.tearDown
AbstractBaseTest.setUp
Test.setUp
test2
Test.tearDown
AbstractBaseTest.tearDown

Vantaggio di questo approccio: gli utenti della classe AbstractBaseTest non possono ignorare accidentalmente i metodi setUp / tearDown. Se vogliono, devono conoscere il nome esatto e possono farlo.

Svantaggio (minore) di questo approccio: gli utenti non possono vedere che stanno accadendo cose prima o dopo il loro setUp / tearDown. Devono sapere che queste cose sono fornite dalla classe astratta. Ma presumo che sia la ragione per cui usano la classe astratta


2
bell'esempio - sarebbe ancora più illustrativo se avessi due metodi @Test, quindi si può vedere che setUp e tearDown avvolgono ogni metodo di test.
Segna l'

Penso che questa sia la base per la migliore risposta al PO, ma dovresti inserire la tua risposta autonoma. Potresti aumentare il tuo esempio per includere anche le alternative suggerite da altri e spiegare perché la tua proposta è superiore?
wachr,

2

Se si cambiano le cose, è possibile dichiarare la propria classe di base astratta e fare in modo che i discendenti dichiarino i metodi setUp e tearDown (senza annotazioni) chiamati nei metodi setUp e tearDown annotati della classe base.


1
non è una cattiva idea, ma non voglio imporre un contratto sui test che non hanno bisogno del loro setUp / tearDown
Joel,

2

È possibile utilizzare l' @BeforeClassannotazione per assicurarsi che setup()venga sempre chiamato per primo. Allo stesso modo, puoi usare l' @AfterClassannotazione per assicurarti che tearDown()sia sempre chiamato per ultimo.

Questo di solito non è raccomandato, ma è supportato .

Non è esattamente quello che vuoi, ma essenzialmente manterrà la tua connessione DB aperta per tutto il tempo in cui i tuoi test sono in esecuzione, quindi alla fine la chiuderà una volta per tutte.


2
In realtà, se dovessi farlo, ti consiglierei di creare un metodo setupDB()e di closeDB()contrassegnarli con @BeforeClasse @AfterClasse sostituire i tuoi metodi prima / dopo con setup()etearDown()
Swati,

I metodi annotati @BeforeClasse @AfterClassdevono essere statici. Che dire del caso, quando vogliamo usare le variabili di istanza all'interno di questi metodi?
Pratik Singhal,

Un avvertimento quando si utilizza @BeforeClasscon Powermock: funziona solo per la prima esecuzione del test. Vedi questo numero: github.com/powermock/powermock/issues/398
Dagmar,

2

Questa non è una risposta alla domanda slogan, ma è una risposta ai problemi menzionati nel corpo della domanda. Invece di usare @Before o @After, cerca in @ org.junit.Rule perché ti dà più flessibilità. ExternalResource (a partire da 4.7) è la regola che ti interesserà di più se gestisci le connessioni. Inoltre, se si desidera un ordine di esecuzione garantito delle regole, utilizzare un RuleChain (a partire da 4.10). Credo che tutti questi fossero disponibili quando è stata posta questa domanda. L'esempio di codice seguente viene copiato dai javadocs di ExternalResource.

 public static class UsesExternalResource {
  Server myServer= new Server();

  @Rule
  public ExternalResource resource= new ExternalResource() {
      @Override
      protected void before() throws Throwable {
          myServer.connect();
         };

      @Override
      protected void after() {
          myServer.disconnect();
         };
     };

  @Test
  public void testFoo() {
      new Client().run(myServer);
     }
 }
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.