Junit prima della lezione (non statico)


84

Esistono best practice per fare in modo che Junit esegua una funzione una volta in un file di test e non dovrebbe essere statico.

come @BeforeClasssulla funzione non statica?

Ecco una brutta soluzione:

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

beh, questo è qualcosa che non voglio fare e sto cercando una soluzione junit integrata.


Bene, ho una gerarchia abbastanza grande di file di test e file di test di base, ho bisogno della possibilità di sovrascrivere questa azione nelle classi di test figlio.
Roman

1
ho avuto lo stesso problema in cui solo il primo di molti test parametrizzati dovrebbe eseguire un login.
dokaspar

5
Si noti che la soluzione "brutta", quella che funziona con JUnit semplice, non tiene conto dei test di tearing.
eskatos

Risposte:


22

Se non vuoi impostare inizializzatori statici per l'inizializzazione una tantum e non sei particolarmente interessato all'uso di JUnit, dai un'occhiata a TestNG. TestNG supporta l'inizializzazione una tantum non statica con una varietà di opzioni di configurazione, tutte utilizzando annotazioni.

In TestNG, questo sarebbe equivalente a:

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

Per lo smontaggio,

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

Per l'equivalente TestNG di JUnit 4 @Beforee @After, puoi usare @BeforeMethode @AfterMethodrispettivamente.


41

Anche una semplice istruzione if sembra funzionare abbastanza bene:

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

        }
    }

 ...

1
Bello e semplice! Ma non riesci a vedere un modo per adattarlo semplicemente per creare un @AfterClassequivalente non statico che si rompe dopo che tutti i test sono stati eseguiti?
Steve Chambers,

1
Vedi qui per un aggiornamento a questo metodo che dovrebbe funzionare per le classi di test che usano l'ereditarietà.
Steve Chambers il

36

Utilizzare un costruttore vuoto è la soluzione più semplice. È ancora possibile sovrascrivere il costruttore nella classe estesa.

Ma non è ottimale con tutta l'eredità. Ecco perché JUnit 4 utilizza invece le annotazioni.

Un'altra opzione è creare un metodo di supporto in una classe factory / util e lasciare che il metodo faccia il lavoro.

Se stai usando Spring, dovresti considerare l'utilizzo @TestExecutionListenersdell'annotazione. Qualcosa di simile a questo test:

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

Spring's AbstractTestExecutionListenercontiene ad esempio questo metodo vuoto che puoi sovrascrivere:

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

NOTA: NON trascurare / perdere DependencyInjectionTestExecutionListenerdurante l'aggiunta di personalizzazioni TestExecutionListeners. Se lo fai, tutti i cavi automatici saranno null.


+1 Questa tecnica ha risolto il mio problema quando volevo utilizzare DbUnit e caricare il set di dati solo una volta per classe
Brad

+1 Questo è perfetto ... per le persone che non sono legate a una versione antica della primavera. :(
Mike Miller

1
Questo verrà beforeTestClass()chiamato prima o dopo l'inizializzazione del contesto?
Diminuisce il

@Dims dopo l' inizializzazione del contesto
Anand Rockzz

7

Usa facilmente @BeforeAllMethods/ @AfterAllMethodsannotations per eseguire un metodo all'interno del contesto dell'istanza (non statico), dove saranno disponibili tutti i valori iniettati.

C'è una libreria di test speciale per questo:

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/

Unica limitazione: funziona solo per i test di primavera .

(Sono lo sviluppatore di questa libreria di test)


0

Non ho mai provato, ma forse puoi creare un costruttore senza argomenti e chiamare la tua funzione da lì?


Questo funzionerebbe, il problema è che ho bisogno della possibilità di sovrascrivere questa azione nelle classi che estendono questa classe di test di base
Roman

@Roman: oh, ora capisco. Aggiungi questo al tuo post, questo commento rende le cose molto più chiare.
Roman

Il costruttore verrà chiamato tutte le volte che sono presenti i casi di test. Per ogni metodo di test, verrà creato un nuovo oggetto della classe Test. Quindi, usare il costruttore non è una soluzione qui
manikanta

Anche questo non funzionerà con l'inserimento di dipendenze che si basa sull'oggetto già costruito.
Mike Miller

0

L'articolo discute 2 soluzioni molto belle per questo problema:

  1. junit "pulito" con Runner personalizzato (usando l'interfaccia ma potresti estenderlo con un'annotazione personalizzata, ad esempio @BeforeInstance)
  2. Ascoltatori di esecuzioni primaverili come menzionato prima da Espen.

0

AGGIORNAMENTO: Si prega di vedere il commento di Cherry per il motivo per cui il suggerimento di seguito è errato. (Sto mantenendo la risposta qui anziché eliminarla poiché il commento potrebbe fornire informazioni utili ad altri sul motivo per cui non funziona.)


Un'altra opzione da considerare se si utilizza l'inserimento di dipendenze (ad esempio Spring) è @PostConstruct. Ciò garantirà che l'inserimento delle dipendenze sia completo, cosa che non sarebbe il caso in un costruttore:

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


7
Molto male soluzione in caso di test JUnit. Junit crea l'istanza della classe di test ogni volta che esegue un metodo di test. Quindi, se ci sono 6 metodi di test in classe, un costruttore di classi @Beforee @Aftermetodi verranno chiamati 6 volte! Quindi in questo contesto @PostConstructsi comporta come @Beforeun'annotazione. Puoi semplicemente testarlo: metti 2 metodi di test nella classe di test, aggiungi @PostConstruct public void init() {System.out.println("started");}e vedi nei log quante volte viene stampato.
Cherry

Per informazioni mi sono appena imbattuto nella documentazione di JUnit che conferma quanto descritto nel commento sopra sulla creazione di un'istanza da parte di JUnit per ogni @Testesecuzione: "Per eseguire il metodo, JUnit costruisce prima una nuova istanza della classe, quindi richiama il metodo annotato".
Steve Chambers,

-2

Usa solo @BeforeClass:

@BeforeClass
public static void init() {
}

Non ha senso initessere non statici perché ogni test viene eseguito in un'istanza separata. L'istanza su cui initviene eseguito non corrisponderebbe all'istanza di alcun test.

L'unico motivo per cui potresti volere che non sia statico è sovrascriverlo nelle sottoclassi, ma puoi farlo anche con metodi statici. Basta usare lo stesso nome e initverrà chiamato solo il metodo della sottoclasse .


2
L'intera domanda riguarda la possibilità di farlo in un modo non statico, che è necessario se hai bisogno di alcune variabili di istanza sulla classe.
Simon Forsberg

@SimonForsberg Sì, e sto dicendo che la domanda è un problema XY. L'operatore ha detto che il problema stava sovrascrivendo il comportamento nelle classi figlio. Se l'esempio necessitava di variabili di istanza, allora potrei suggerire qualcos'altro.
fgb


@SimonForsberg Questo è il commento di cui stavo parlando. Che ne dici?
fgb
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.