Come eseguire i metodi di test in ordine specifico in JUnit4?


413

Voglio eseguire metodi di prova che sono annotati @Testin ordine specifico.

Per esempio:

public class MyTest {
    @Test public void test1(){}
    @Test public void test2(){}
}

Voglio assicurarmi di correre test1()prima di test2()ogni corsaMyTest , ma non sono riuscito a trovare annotazioni simili @Test(order=xx).

Penso che sia una funzione abbastanza importante per JUnit, se l'autore di JUnit non desidera la funzione di ordine , perché?


2
Mi sembrano essere eseguiti nell'ordine in cui compaiono nel file sorgente.
Marchese di Lorne,

84
Non si devono mai scrivere test che devono essere eseguiti in un ordine specificato. È davvero una brutta pratica. Ogni test dovrebbe essere in grado di funzionare in modo indipendente.
Apfelsaft,

5
@EJP questo era quasi universalmente vero per java pre 7. Pre 7, la maggior parte delle JVM lo ha fatto, ma non è mai stato garantito. Le JVM Java 7 possono restituire i metodi in un ordine non deterministico.
Matthew Farwell,

17
Aggirare. Rimuovi @Test dai tuoi casi di test, convertili come funzioni private, quindi crea un singolo caso di test e chiama le funzioni private in ordine.
Simon Guo,

12
La rimozione di @Test dai casi di test confonderà il rapporto JUnit. A proposito, far rispettare un ordine specifico è una cattiva pratica per i test unitari ma non necessariamente una cattiva pratica per i test di integrazione . La scelta migliore (non ideale) è di annotare la classe @FixMethodOrder(MethodSorters.NAME_ASCENDING), conservare l' @Testannotazione per tutti i metodi di prova e rinominarli in ordine alfabetico in base all'ordine di esecuzione desiderato, ad es t1_firstTest(). t2_secondTest(), Ecc.
MisterStrickland

Risposte:


238

Penso che sia una funzione abbastanza importante per JUnit, se l'autore di JUnit non desidera la funzione di ordine, perché?

Non sono sicuro che ci sia un modo chiaro per farlo con JUnit, per quanto ne so JUnit presuppone che tutti i test possano essere eseguiti in un ordine arbitrario. Dalle FAQ:

Come si usa un dispositivo di prova?

(...) L'ordinamento delle invocazioni del metodo di test non è garantito , quindi testOneItemCollection () potrebbe essere eseguito prima di testEmptyCollection (). (...)

Perché è così? Bene, credo che subordinare l'ordine ai test sia una pratica che gli autori non vogliono promuovere. I test dovrebbero essere indipendenti, non dovrebbero essere accoppiati e violare ciò renderà le cose più difficili da mantenere, interromperà la capacità di eseguire i test individualmente (ovviamente), ecc.

Detto questo, se vuoi davvero andare in questa direzione, considera l'utilizzo di TestNG poiché supporta l'esecuzione nativa di metodi di test in qualsiasi ordine arbitrario (e cose come specificare che i metodi dipendono da gruppi di metodi). Cedric Beust spiega come eseguire questa operazione in ordine di esecuzione dei test in testng .


14
O hai due test indipendenti o hai solo un test e dovresti codificare come tale.
Jon Freedman,

2
@JonFreedman, come capisco la domanda, non è un caso di test interdipendenti, ma solo di avere una specifica di cose da testare e di voler apparire i risultati in quell'ordine.
Jon Bright,

174
Riesco a capire di non applicare l'ordine per i test unitari, tuttavia quando si utilizza JUnit per scrivere test di integrazione sarebbe bello poter specificare l'ordine di esecuzione dei test. Ad esempio, eseguire prima il test di accesso.
Brian DiCasa,

13
@BrianD. login è probabilmente un "dispositivo" invece di un test che deve essere eseguito prima di tutti gli altri. Probabilmente scriverò una BeforeClass che accederà e quindi scriverò i test da eseguire in qualsiasi ordine.
marcospereira,

47
L'implicazione "i test dovrebbero essere indipendenti => i test dovrebbero essere ORDER indipendenti" non è vera. Valuta la valutazione automatica dei compiti a casa dello studente. Voglio testare prima la loro soluzione per input più piccoli e per input più grandi. Quando la soluzione non riesce per input più piccoli (per limite di tempo / memoria), perché i test dovrebbero essere eseguiti per input più grandi?
mirelon,

96

Se si elimina l'istanza esistente di Junit e si scarica JUnit 4.11 o versione successiva nel percorso di compilazione, il codice seguente eseguirà i metodi di test nell'ordine dei nomi, ordinati in ordine crescente:

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class SampleTest {

    @Test
    public void testAcreate() {
        System.out.println("first");
    }
    @Test
    public void testBupdate() {
        System.out.println("second");
    }
    @Test
    public void testCdelete() {
        System.out.println("third");
    }
}

8
In realtà abbiamo provato un metodo simile test_001_login (), ad esempio, ma sebbene funzioni principalmente per preservare l'ordine, non è garantito, abbiamo diverse istanze per test in cui 004, 005 e 006 vengono eseguiti dopo 007. dici "WTF !," ed esegui StackOverflow per le risposte.
Max P Magee,

Fantastico - disponibile in JUnit 4.12
DtechNet il

1
nei miei test: testAcase - ha funzionato, test_A_case / testA_case - no!
Rodislav Moldovan,

6
Ho provato questo parametro di annotazione "MethodSorters.JVM", ad esempio "@FixMethodOrder (MethodSorters.JVM)". Dall'API: JVM: lascia i metodi di test nell'ordine restituito da JVM. Funziona bene per quello che sto facendo (CRUD), esegue i metodi di prova nell'ordine in cui sono scritti. +1
Edvinauskas,

1
Questa annotazione è davvero una risposta, ma ha la precisazione che non è definita (in Junit 4.12) con @Inheritede quindi diventa inefficace sulla mia AbstractTestCaseclasse genitore.
AbVog,

49

Se l'ordine è importante, dovresti effettuare tu stesso l'ordine.

@Test public void test1() { ... }
@Test public void test2() { test1(); ... }

In particolare, è necessario elencare alcune o tutte le possibili permutazioni dell'ordine da testare, se necessario.

Per esempio,

void test1(); 
void test2(); 
void test3(); 


@Test
public void testOrder1() { test1(); test3(); }

@Test(expected = Exception.class)
public void testOrder2() { test2(); test3(); test1(); }

@Test(expected = NullPointerException.class)
public void testOrder3() { test3(); test1(); test2(); }

Oppure, un test completo di tutte le permutazioni:

@Test
public void testAllOrders() {
    for (Object[] sample: permute(1, 2, 3)) {
        for (Object index: sample) {
            switch (((Integer) index).intValue()) {
                case 1: test1(); break; 
                case 2: test2(); break; 
                case 3: test3(); break; 
            }
        }
    }
}

Qui, permute()c'è una semplice funzione che itera tutte le possibili permuazioni in una raccolta di array.


Ma cosa succede se i test in file diversi?
Oleg Abrazhaev,

3
Nel primo blocco di codice, test2viene eseguito test1 nuovamente . Junit potrebbe ancora funzionare test2prima test1. Questo probabilmente non è quello che intendevi e non è una risposta valida alla domanda.
Toolforger,

47

La migrazione a TestNG sembra il modo migliore, ma non vedo una soluzione chiara qui per jUnit. Ecco la soluzione / formattazione più leggibile che ho trovato per jUnit:

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class SampleTest {
    @Test
    void stage1_prepareAndTest(){};

    @Test
    void stage2_checkSomething(){};

    @Test
    void stage2_checkSomethingElse(){};

    @Test
    void stage3_thisDependsOnStage2(){};

    @Test
    void callTimeDoesntMatter(){}
}

Questo assicura che i metodi stage2 siano chiamati dopo quelli stage1 e prima di quelli stage3.


5
Questo approccio è carino, ma sarebbe valido menzionare che se hai più di 10 test non funzionerà bene se non aggiungi un 0prefisso, ad esempiovoid stage01_prepareAndTest(){ }
EliuX

Se hai più di 10 fasi (non test) - Sì. Preferisco limitare il numero di stadi e fare più test in ogni fase, quando ciò è possibile.
joro,

18

È uno dei problemi principali che ho riscontrato quando ho lavorato su Junit e ho trovato la seguente soluzione che funziona bene per me:

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;

public class OrderedRunner extends BlockJUnit4ClassRunner {

    public OrderedRunner(Class<?> clazz) throws InitializationError {
        super(clazz);
    }

    @Override
    protected List<FrameworkMethod> computeTestMethods() {
        List<FrameworkMethod> list = super.computeTestMethods();
        List<FrameworkMethod> copy = new ArrayList<FrameworkMethod>(list);
        Collections.sort(copy, new Comparator<FrameworkMethod>() {

            @Override
            public int compare(FrameworkMethod f1, FrameworkMethod f2) {
                Order o1 = f1.getAnnotation(Order.class);
                Order o2 = f2.getAnnotation(Order.class);

                if (o1 == null || o2 == null) {
                    return -1;
                }

                return o1.order() - o2.order();
            }
        });
        return copy;
    }
}

crea anche un'interfaccia come di seguito:

 @Retention(RetentionPolicy.RUNTIME)


@Target({ ElementType.METHOD})

public @interface Order {
public int order();
}

Supponiamo ora di avere una classe A in cui hai scritto diversi casi di test come di seguito:

(@runWith=OrderRunner.class)
Class A{
@Test
@Order(order = 1)

void method(){

//do something

}

}

Quindi l'esecuzione inizierà dal metodo denominato "method ()". Grazie!


Uso di un altro Runner JUnit con PowerMock Dalla versione 1.6.0 PowerMock supporta la delega dell'esecuzione del test a un altro Runner JUnit senza utilizzare una regola JUnit. Ciò lascia l'esecuzione effettiva del test a un altro corridore di tua scelta. @RunWith(PowerMockRunner.class) @PowerMockRunnerDelegate(OrderedRunner.class)
Kyriakos Georgiopoulos,

11

JUnit attualmente consente ai metodi di test di eseguire l'ordinamento utilizzando le annotazioni di classe:

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@FixMethodOrder(MethodSorters.JVM)
@FixMethodOrder(MethodSorters.DEFAULT)

Per impostazione predefinita, i metodi di prova sono eseguiti in ordine alfabetico. Quindi, per impostare metodi specifici, puoi chiamarli come:

a_TestWorkUnit_WithCertainState_ShouldDoSomething b_TestWorkUnit_WithCertainState_ShouldDoSomething c_TestWorkUnit_WithCertainState_ShouldDoSomething

Puoi trovare esempi qui .



6

Guarda un rapporto JUnit. JUnit è già organizzato per pacchetto. Ogni pacchetto ha (o può avere) classi TestSuite, ognuna delle quali a sua volta esegue più TestCase. Ciascun TestCase può avere più metodi di test del modulopublic void test*() , ognuno dei quali diventerà effettivamente un'istanza della classe TestCase a cui appartengono. Ogni metodo di test (istanza TestCase) ha un nome e un criterio pass / fail.

Ciò che la mia gestione richiede è il concetto di singoli elementi TestStep , ognuno dei quali riporta i propri criteri di superamento / fallimento. Il fallimento di qualsiasi fase del test non deve impedire l'esecuzione delle fasi successive del test.

In passato, gli sviluppatori di test nella mia posizione organizzavano le classi TestCase in pacchetti che corrispondono alle parti del prodotto in prova, creavano una classe TestCase per ciascun test e trasformavano ciascun metodo di test in un "passaggio" separato nel test, completo con i propri criteri pass / fail nell'output JUnit. Ciascun TestCase è un "test" autonomo, ma i singoli metodi o test "passaggi" all'interno del TestCase devono avvenire in un ordine specifico.

I metodi TestCase sono stati i passaggi di TestCase e i progettisti di test hanno ottenuto un criterio di superamento / fallimento separato per fase di test. Ora i passaggi del test sono confusi e i test (ovviamente) falliscono.

Per esempio:

Class testStateChanges extends TestCase

public void testCreateObjectPlacesTheObjectInStateA()
public void testTransitionToStateBAndValidateStateB()
public void testTransitionToStateCAndValidateStateC()
public void testTryToDeleteObjectinStateCAndValidateObjectStillExists()
public void testTransitionToStateAAndValidateStateA()
public void testDeleteObjectInStateAAndObjectDoesNotExist()
public void cleanupIfAnythingWentWrong()

Ogni metodo di prova afferma e riporta i propri criteri di superamento / fallimento separati. La compressione di questo in "un unico metodo di prova" per motivi di ordinazione perde la granularità dei criteri pass / fail di ogni "passaggio" nel rapporto di riepilogo di JUnit. ... e questo sconvolge i miei manager. Attualmente stanno chiedendo un'altra alternativa.

Qualcuno può spiegare come una JUnit con l'ordinamento del metodo di test criptato supporterebbe criteri separati pass / fail di ciascuna fase del test sequenziale, come esemplificato sopra e richiesto dalla mia direzione?

Indipendentemente dalla documentazione, vedo questo come una grave regressione nel framework JUnit che sta rendendo la vita difficile a molti sviluppatori di test.


6

Aggiornamento di JUnit 5 (e la mia opinione)

Penso che sia una funzione abbastanza importante per JUnit, se l'autore di JUnit non desidera la funzione di ordine, perché?

Per impostazione predefinita, le librerie di unit test non tentano di eseguire i test nell'ordine che si verifica nel file di origine.
JUnit 5 come JUnit 4 funziona in questo modo. Perché ? Perché se l'ordine conta, significa che alcuni test sono accoppiati tra loro e questo è indesiderabile per i test unitari .
Quindi la @Nestedfunzione introdotta da JUnit 5 segue lo stesso approccio predefinito.

Ma per i test di integrazione, l'ordine del metodo di test può essere importante poiché un metodo di test può cambiare lo stato dell'applicazione in un modo previsto da un altro metodo di test. Ad esempio, quando si scrive un test di integrazione per un'elaborazione del checkout e-shop, il primo metodo di test da eseguire è la registrazione di un client, il secondo è l'aggiunta di articoli nel carrello e l'ultimo il checkout. Se il test runner non rispetta tale ordine, lo scenario di test è difettoso e fallirà.
Quindi in JUnit 5 (dalla versione 5.4) hai la stessa possibilità di impostare l'ordine di esecuzione annotando la classe di test @TestMethodOrder(OrderAnnotation.class)e specificando l'ordine con @Order(numericOrderValue)i metodi che contano.

Per esempio :

@TestMethodOrder(OrderAnnotation.class) 
class FooTest {

    @Order(3)
    @Test
    void checkoutOrder() {
        System.out.println("checkoutOrder");
    }

    @Order(2)
    @Test
    void addItemsInBasket() {
        System.out.println("addItemsInBasket");
    }

    @Order(1)
    @Test
    void createUserAndLogin() {
        System.out.println("createUserAndLogin");
    }
}

Produzione :

createUserAndLogin

addItemsInBasket

checkoutOrder

A proposito, specificare non @TestMethodOrder(OrderAnnotation.class)sembra necessario (almeno nella versione 5.4.0 che ho testato).

Nota a margine
Informazioni sulla domanda: JUnit 5 è la scelta migliore per scrivere test di integrazione? Non penso che dovrebbe essere il primo strumento da considerare (Cucumber and co possono spesso apportare valore e funzionalità più specifici per i test di integrazione) ma in alcuni casi di test di integrazione, il framework JUnit è sufficiente. Questa è una buona notizia dell'esistenza della funzione.


4

Non sono sicuro di essere d'accordo, se desidero testare "Caricamento file" e quindi "Dati inseriti da Caricamento file", perché non vorrei che fossero indipendenti l'uno dall'altro? Perfettamente ragionevole, penso di essere in grado di eseguirli separatamente invece di avere entrambi in un caso di test Goliath.


3

Quello che vuoi è perfettamente ragionevole quando i casi di test vengono eseguiti come una suite.

Purtroppo non c'è tempo per dare una soluzione completa in questo momento, ma dai un'occhiata alla classe:

org.junit.runners.Suite

Ciò consente di chiamare casi di test (da qualsiasi classe di test) in un ordine specifico.

Questi potrebbero essere utilizzati per creare test funzionali, di integrazione o di sistema.

In questo modo i test unitari sono privi di un ordine specifico (come raccomandato), indipendentemente dal fatto che vengano eseguiti in questo modo o meno, quindi riutilizzare i test come parte di un'immagine più ampia.

Riutilizziamo / ereditiamo lo stesso codice per test di unità, integrazione e sistema, a volte guidati dai dati, a volte commessi e talvolta eseguiti come suite.


2
Non hai tempo di completare questa risposta dal 2014? ;)
Charlie

2

Vedi la mia soluzione qui: "Junit e java 7."

In questo articolo descrivo come eseguire i test junit in ordine - "proprio come nel tuo codice sorgente". I test verranno eseguiti, in modo che i metodi di test vengano visualizzati nel file di classe.

http://intellijava.blogspot.com/2012/05/junit-and-java-7.html

Ma come ha detto Pascal Thivent, questa non è una buona pratica.


Avevo visto il tuo post sul blog (in russo!), Ma è troppo complicato.
Nicolas Barbulesco,

1
@NicolasBarbulesco Ho due blog (rus e eng). È troppo complicato, perché non devi creare test con dipendenza dell'ordine di esecuzione. La mia soluzione è una soluzione alternativa, ma la soluzione reale è quella di rimuovere quella dipendenza.
kornero,

1
Questo post non contiene una risposta effettiva. Per favore, considera di aggiungere almeno la spiegazione di base, oltre al link.
impostazione predefinita


0

Ho letto alcune risposte e accetto che non è la migliore prassi, ma il modo più semplice per ordinare i test - e il modo in cui JUnit esegue i test per impostazione predefinita è in ordine alfabetico in ordine crescente.

Quindi basta nominare i test nell'ordine alfabetico desiderato. Si noti inoltre che il nome del test deve iniziare con il test parola. Fai attenzione ai numeri

test12 verrà eseguito prima di test2

così:

testA_MyFirstTest testC_ThirdTest testB_ATestThatRunsSecond


0

Dai un'occhiata a questo: https://github.com/TransparentMarket/junit . Esegue il test nell'ordine in cui sono specificati (definiti nel file di classe compilato). Inoltre presenta una suite AllTest per eseguire prima i test definiti dal sottopacchetto. Utilizzando l'implementazione di AllTest si può estendere la soluzione anche al filtraggio delle proprietà (si usavano le annotazioni @Fast ma quelle non erano ancora state pubblicate).


0

Ecco un'estensione di JUnit che può produrre il comportamento desiderato: https://github.com/aafuks/aaf-junit

So che ciò è contrario agli autori della filosofia JUnit, ma quando si utilizza JUnit in ambienti che non sono test unitari severi (come praticato in Java), ciò può essere molto utile.


0

Sono finito qui pensando che i miei test non fossero stati eseguiti in ordine, ma la verità è che il casino era nei miei lavori asincroni. Quando si lavora con la concorrenza, è necessario eseguire anche controlli di concorrenza tra i test. Nel mio caso, lavori e test condividono un semaforo, quindi i test successivi si bloccano fino a quando il lavoro in esecuzione rilascia il blocco.

So che questo non è completamente correlato a questa domanda, ma forse potrebbe aiutare a risolvere il problema corretto


0

puoi usare uno di questi codici:

@FixMethodOrder(MethodSorters.JVM)OR `@FixMethodOrder(MethodSorters.DEFAULT)` OR `@FixMethodOrder(MethodSorters.NAME_ASCENDING)` before your test class like this:


@FixMethodOrder(MethodSorters.NAME_ASCENDING)


public class BookTest { ...}
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.