Junit: esegui il metodo di configurazione una volta


Risposte:


205

Anche se sono d'accordo con @assylias che l'utilizzo @BeforeClassè una soluzione classica non è sempre conveniente. Il metodo annotato con @BeforeClassdeve essere statico. È molto scomodo per alcuni test che richiedono un'istanza di test case. Ad esempio, test basati sulla primavera che utilizzano @Autowiredper lavorare con i servizi definiti nel contesto della primavera.

In questo caso utilizzo personalmente il setUp()metodo regolare annotato con @Beforeannotazione e gestisco il mio flag personalizzato static(!) boolean:

private static boolean setUpIsDone = false;
.....
@Before
public void setUp() {
    if (setUpIsDone) {
        return;
    }
    // do the setup
    setUpIsDone = true;
}

10
Aggiungendo al commento di Kenny Cason sul perché deve essere statico. Deve essere statico perché JUnit istanzia una nuova istanza della classe di test per ogni metodo @Test. La variabile di istanza verrà reimpostata al valore predefinito (false) per ogni istanza se non è statica. Vedi per maggiori informazioni: martinfowler.com/bliki/JunitNewInstance.html
dustin.schultz

2
Funziona tranne nel caso in cui il setUp()metodo è in una superclasse - ho pubblicato una risposta di seguito tentando di risolverlo.
Steve Chambers,

4
Esito a dirlo a qualcuno con un rappresentante 84k, ma BeforeClass in realtà non risponde alla domanda: BeforeClass viene eseguito all'inizio di ogni classe di test. Ma l'OP ne ha chiesto uno che venga eseguito "solo una volta prima di tutti i test". La soluzione proposta potrebbe farlo, ma dovresti fare in modo che tutte le classi di test estendano una classe "CommonTest" ...
mike rodent

1
@mikerodent, IMHO OP ha chiesto informazioni su tutti i test all'interno del suo test case, non tutti i test in generale. Quindi, il tuo commento è meno rilevante. A proposito, non preoccuparti di dire nulla a nessuno anche se la sua reputazione è alta. Almeno questo è quello che faccio :). E la mia reputazione era notevolmente inferiore ad agosto 2012 quando ho risposto alla domanda.
AlexR

Non funziona nel mio caso, le variabili inizializzate nel setup vengono reimpostate dopo ogni test, quindi è inutile inizializzarle solo una volta.
Aphax

89

Puoi usare l' BeforeClassannotazione :

@BeforeClass
public static void setUpClass() {
    //executed only once, before the first test
}

12
Non posso usarlo, ho pochi metodi di installazione basati su componenti non statici come getClass ()
Bober02

1
@ Bober02 BeforeClass deve essere davvero statico. Se non puoi usarlo, l'altra risposta fornisce una soluzione alternativa.
assylias

2
Sicuro che non puoi usare al TheClassYouWant.classposto della tua chiamata getClass ()? Questo è vero e proprio Java: String.class.getName().
stolsvik


1
@mikerodent Ho capito la domanda come "tutti i test della classe" - ma hai ragione, potrebbe non essere quello che voleva l'OP.
assylias

29

JUnit 5 ora ha un'annotazione @BeforeAll:

Indica che il metodo annotato deve essere eseguito prima di tutti i metodi @Test nella classe corrente o nella gerarchia di classi; analogo a @BeforeClass di JUnit 4. Tali metodi devono essere statici.

Le annotazioni del ciclo di vita di JUnit 5 sembrano aver finalmente capito bene! Puoi indovinare quali annotazioni sono disponibili senza nemmeno guardare (ad esempio @BeforeEach @AfterAll)


6
Ha lo stesso problema era @BeforeClass, deve essere static. La soluzione di IMO @ AlexR è migliore.
zengr

@zengr tende ad essere d'accordo con te: come ho detto ad AlexR, la sua soluzione richiede che tutte le classi di test siano sottoclasse da una classe CommonTest se deve essere eseguita una sola volta. Ma è semplice quanto semplice può essere, e IMHO probabilmente non dovresti usare una soluzione fornita dal framework "fantasia" quando è disponibile un semplice meccanismo dal linguaggio. A meno che non ci sia una buona ragione, ovviamente. Inoltre, usare una cosa semplice come la sua, con un buon nome del tipo "fa quello che dice sulla latta", aiuta con la leggibilità.
mike rodent

Detto questo, sempre IMHO, sembra esserci molta più giustificazione per avere un'annotazione "AfterAll": sarebbe molto difficile e artificioso escogitare un meccanismo per rilevare quando tutti i test sono stati eseguiti. Al contrario, ovviamente, i puristi probabilmente diranno che non dovresti mai dover fare una "pulizia finale", cioè che ogni "tearDown" dovrebbe lasciare tutte le risorse in uno stato incontaminato ... e probabilmente hanno ragione!
mike rodent

Funziona con Maven dove sono presenti più moduli, ciascuno con i propri test?
Mark Boon

@mike rodent, nel mio caso la configurazione e la rimozione dei file di test nel filesystem prima / dopo ogni test sembra portare a deadlock sui file. Per ora, sono arrivato in modo indipendente alla soluzione di AlexR per la configurazione una volta. Ho due flag statici, già impostato e sporco. setup () chiama cleanup () se viene rilevato inizialmente uno stato sporco o se un errore di installazione porta a uno stato sporco. Per ripulire dopo aver eseguito i test, li eseguo di nuovo. Disordinato, per niente ideale, non nel nostro processo di costruzione. Sto ancora cercando un modo migliore (jUnit 4.12).
Rebeccah

9

Quando si setUp()trova in una superclasse della classe di test (ad esempio AbstractTestBasesotto), la risposta accettata può essere modificata come segue:

public abstract class AbstractTestBase {
    private static Class<? extends AbstractTestBase> testClass;
    .....
    public void setUp() {
        if (this.getClass().equals(testClass)) {
            return;
        }

        // do the setup - once per concrete test class
        .....
        testClass = this.getClass();
    }
}

Questo dovrebbe funzionare per un singolo setUp()metodo non statico , ma non sono in grado di produrre un equivalente tearDown()senza perdersi in un mondo di riflessioni complesse ... Bounty punta a chiunque possa!


3

Modifica: ho appena scoperto durante il debug che la classe viene istanziata anche prima di ogni test. Immagino che l'annotazione @BeforeClass sia la migliore qui.

Puoi anche configurarlo sul costruttore, la classe di test è una classe dopotutto. Non sono sicuro che sia una cattiva pratica perché quasi tutti gli altri metodi sono annotati, ma funziona. Potresti creare un costruttore come quello:

public UT () {
    // initialize once here
}
@Test
// Some test here...

Il ctor verrà chiamato prima dei test perché non sono statici.



0

La mia soluzione sporca è:

public class TestCaseExtended extends TestCase {

    private boolean isInitialized = false;
    private int serId;

    @Override
    public void setUp() throws Exception {
        super.setUp();
        if(!isInitialized) {
            loadSaveNewSerId();
            emptyTestResultsDirectory();
            isInitialized = true;
        }
    }

   ...

}

Lo uso come base per tutti i miei casi di test.


public class TestCaseExtended estende TestCase {private static boolean isInitialized = false; private static TestCaseExtended caseExtended; private int serId; @Override public void setUp () genera un'eccezione {super.setUp (); if (! isInitialized) {caseExtended = new TestCaseExtended (); caseExtended.loadSaveNewSerId (); caseExtended.emptyTestResultsDirectory (); isInitialized = true; }}
Obi Two

0

Se non vuoi forzare una dichiarazione di una variabile che è impostata e controllata su ogni subtest, aggiungerlo a un SuperTest potrebbe fare:

public abstract class SuperTest {

    private static final ConcurrentHashMap<Class, Boolean> INITIALIZED = new ConcurrentHashMap<>();
    protected final boolean initialized() {
        final boolean[] absent = {false};
        INITIALIZED.computeIfAbsent(this.getClass(), (klass)-> {
            return absent[0] = true;
        });
        return !absent[0];
    }
}



public class SubTest extends SuperTest {
    @Before
    public void before() {
        if ( super.initialized() ) return;

         ... magic ... 
    }

}

0

Ho risolto questo problema in questo modo:

Aggiungi alla tua classe astratta di base (intendo la classe astratta in cui inizializzi il tuo driver nel metodo setUpDriver () ) questa parte di codice:

private static boolean started = false;
static{
    if (!started) {
        started = true;
        try {
            setUpDriver();  //method where you initialize your driver
        } catch (MalformedURLException e) {
        }
    }
}

E ora, se le classi di test si estendono dalla classe astratta Base -> il metodo setUpDriver () verrà eseguito prima del primo @Test solo UNA volta per esecuzione.


0

Usa il metodo @PostConstruct di Spring per eseguire tutto il lavoro di inizializzazione e questo metodo viene eseguito prima che venga eseguito uno qualsiasi dei @Test

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.