2012-06-22 16 views
16

Sto utilizzando jUnit per gestire i test di integrazione per un'applicazione che accede a un database. Poiché l'impostazione dei dati di test è un'operazione che richiede molto tempo, l'ho eseguita nel metodo @BeforeClass, che viene eseguito solo una volta per classe di test (in contrasto con il metodo @Before, che viene eseguito una volta per metodo di prova).Con jUnit 4, posso parametrizzare @BeforeClass?

Ora voglio provare alcune diverse permutazioni per la configurazione del livello dati, eseguendo tutti i miei test su ogni diversa configurazione. Questo sembra un uso naturale del test runner Parameterized. Il problema è che Parameterized fornisce i parametri al costruttore della classe e il metodo @BeforeClass è astratto e viene chiamato prima del costruttore della classe.

Qualche domanda,

Vuol Parameterized chiamata al metodo @BeforeClass per ogni permutazione dei parametri, o lo fa chiamare solo una volta?

Se il metodo @BeforeClass viene chiamato ripetutamente, esiste un modo per accedere ai valori dei parametri dall'interno di esso?

Se nessuno di questi, cosa suggerisce la gente come il miglior approccio alternativo a questo problema?

+0

Vedere se http://code.google.com/p/junitparams/ può aiutare – Jayan

+0

Quindi non c'è ancora modo di farlo? –

+1

L'effetto può essere eseguito da un corridore di prova personalizzato. Tipicamente si sottoclasse BlockJUnit4ClassRunner. –

risposta

1

@BeforeClass viene chiamato solo una volta nell'esempio. Che ha senso dato il nome - prima della lezione!

Se i test richiedono dati diversi, ci sono due scelte che posso pensare:

  1. set up che i dati in @Before quindi è test specifico
  2. Gruppo i test che si desidera eseguire con gli stessi dati in classi di test separate e utilizzare @BeforeClass per ognuno.
+1

Grazie per aver chiarito la mia comprensione di @ BeforeClass. Sfortunatamente, i due approcci che hai delineato non risolvono il mio problema più grande. Il problema con l'approccio 1 è che ho un'iniziale one-off di inizializzazione che cambia da uno scenario all'altro, ma che non voglio dover ripetere per ogni test. Il problema con l'approccio 2 è che dovrei scrivere una classe di test diversa per ogni scenario, anche se sto eseguendo gli stessi test. –

+1

Dan, per l'opzione # 2, è possibile creare una sottoclasse. Hanno gli "stessi test" nella superclasse e quindi ciascuna sottoclasse riflette lo scenario. In altre parole, le sottoclassi contengono @BeforeClass –

+0

Questo approccio funzionerebbe tecnicamente, ma non lo trovo attraente, dal momento che ho già dozzine di classi di test e mi aspetto di avere centinaia di volte –

-1

Si potrebbe eseguire l'inizializzazione in un metodo @Before, scrivendo su una variabile di istanza ma testando null.

@RunWith(value = Parameterized.class) 
public class BigThingTests { 
    private BigThing bigThing; 

    @Before 
    public void createBitThing() { 
    if (bigThing == null) { 
     bigThing = new BigThing(); 
    } 
    } 

... 
} 

Una nuova istanza di BigThingTests viene creata per ogni insieme di parametri, e bigThing viene impostata su null con ogni nuova istanza. Il runner Parameterized è a thread singolo, quindi non devi preoccuparti di più inizializzazioni.

+0

this.bigThing sarà sempre nullo, quando ricevi una nuova istanza di BigThingTests per ogni @Test. –

0

È possibile chiamare questa logica di inizializzazione nel costruttore della classe di test. Tieni traccia dell'ultimo parametro utilizzato in una variabile statica. Quando cambia, imposta la classe per il nuovo parametro.

Non riesco a pensare a un equivalente per AfterClass.

+1

L'uso del costruttore invece di '@ Before' non aiuta, poiché il test runner standard JUnit 4 crea una nuova istanza della classe di test per ogni metodo di test. Quindi avrei ancora il problema che il mio costoso codice di setup è chiamato una volta per ogni metodo e permutazione dei peramer, piuttosto che una sola volta per ogni permutazione dei parametri. –

+0

Buona presa. Ho aggiornato la mia risposta (e le prove su cui sto lavorando con lo stesso problema). – TREE

3

Penso che avrete bisogno di un test runner personalizzato. Sto avendo lo stesso problema che stai avendo (bisogno di eseguire gli stessi test utilizzando configurazioni multiple e costose). Avresti bisogno di un modo per parametrizzare la configurazione, magari usando le annotazioni @Parameter simili a quelle usate dal Runner parametrizzato ma sui campi dei membri statici invece dei campi di istanza. Il runner personalizzato dovrebbe trovare tutti i campi dei membri statici con l'annotazione @Parameter e quindi eseguire la classe di test (probabilmente utilizzando il BlockJunit4ClassRunner di base) una volta per il campo statico @Parameter. Il campo @Parameter dovrebbe probabilmente essere un @ClassRule.

Andy on Software ha svolto un buon lavoro nello sviluppo di test runner personalizzati e lo spiega in modo molto chiaro in questi post del blog here e here.

+0

+1 per @ClassRule che funziona perfettamente senza interferire con la funzionalità parametrizzata o richiedere un runner personalizzato – bstoney

0

Questa è una vecchia domanda, ma ho dovuto risolvere un problema probabilmente simile. Sono andato con la soluzione qui sotto per ora, che essenzialmente è un'implementazione della risposta (aggiornata) di TREE con l'uso di una classe base astratta generica per evitare la duplicazione ogni volta che è necessario questo meccanismo.

I test concreti forniscono un metodo @Parameters che restituisce un iterabile di array di elementi singoli contenenti un Fornitore < T> ciascuno. Questi fornitori vengono quindi eseguiti esattamente una volta per input effettivo richiesto dai metodi di test concreti.

@RunWith(Parameterized.class) 
public class AbstractBufferedInputTest<T> { 

private static Object INPUT_BUFFER; 

private static Object PROVIDER_OF_BUFFERED_INPUT; 

private T currentInput; 

@SuppressWarnings("unchecked") 
public AbstractBufferedInputTest(Supplier<T> inputSuppler) { 
    if (PROVIDER_OF_BUFFERED_INPUT != inputSuppler) { 
     INPUT_BUFFER = inputSuppler.get(); 
     PROVIDER_OF_BUFFERED_INPUT = inputSuppler; 
    } 
    currentInput = (T) INPUT_BUFFER; 
} 

/** 
* 
* @return the input to be used by test methods 
*/ 
public T getCurrentInput() { 
    return currentInput; 
} 

} 
Problemi correlati