Wie werden Testmethoden in einer bestimmten Reihenfolge in JUnit4 ausgeführt?

413

Ich möchte Testmethoden ausführen, die @Testin einer bestimmten Reihenfolge mit Anmerkungen versehen sind .

Zum Beispiel:

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

Ich möchte sicherstellen, dass es test1()vor test2()jedem Start ausgeführt MyTestwird, konnte jedoch keine Anmerkungen wie finden @Test(order=xx).

Ich denke , es ist ganz wichtiges Merkmal für JUnit, wenn Autor von JUnit nicht die will , um Funktion , warum?

Gy 声 远 Shengyuan Lu
quelle
2
Sie scheinen mir in der Reihenfolge ausgeführt zu werden, in der sie in der Quelldatei erscheinen.
Marquis von Lorne
84
Sie sollten niemals Tests schreiben, die in einer bestimmten Reihenfolge ausgeführt werden müssen. Das ist wirklich schlechte Praxis. Jeder Test sollte unabhängig laufen können.
Apfelsaft
5
@EJP Dies traf fast überall auf Java vor 7 zu. Vor 7 haben die meisten JVMs dies getan, aber es wurde nie garantiert. Java 7-JVMs können die Methoden in einer nicht deterministischen Reihenfolge zurückgeben.
Matthew Farwell
17
Umgehen. Entfernen Sie @Test aus Ihren Testfällen, konvertieren Sie sie als private Funktionen, erstellen Sie einen einzelnen Testfall und rufen Sie nacheinander private Funktionen auf.
Simon Guo
12
Wenn Sie @Test aus den Testfällen entfernen, wird der JUnit-Bericht durcheinander gebracht. Die Durchsetzung einer bestimmten Anordnung ist übrigens eine schlechte Praxis für Unit-Tests, aber nicht unbedingt eine schlechte Praxis für Integrationstests . Die beste Wahl (nicht ideal) ist die Klasse mit zu annotieren @FixMethodOrder(MethodSorters.NAME_ASCENDING), halten Sie die @TestAnmerkung für alle Prüfverfahren und sie alphabetisch umbenennen auf die gewünschte Reihenfolge der Ausführung abhängig, zum Beispiel t1_firstTest(), t2_secondTest()usw.
MisterStrickland

Antworten:

238

Ich denke, es ist eine ziemlich wichtige Funktion für JUnit. Wenn der Autor von JUnit die Bestellfunktion nicht möchte, warum?

Ich bin mir nicht sicher, ob es einen sauberen Weg gibt, dies mit JUnit zu tun. Meines Wissens geht JUnit davon aus, dass alle Tests in einer beliebigen Reihenfolge durchgeführt werden können. Aus den FAQ:

Wie verwende ich ein Testgerät?

(...) Die Reihenfolge der Aufrufe von Testmethoden kann nicht garantiert werden , daher kann testOneItemCollection () vor testEmptyCollection () ausgeführt werden. (...)

Wieso ist es so? Nun, ich glaube, dass es eine Praxis ist, die Autoren nicht fördern wollen, die Reihenfolge der Tests abhängig zu machen. Tests sollten unabhängig sein, sie sollten nicht gekoppelt sein und Verstöße gegen diese Bestimmungen erschweren die Wartung, beeinträchtigen die Fähigkeit, Tests (offensichtlich) einzeln durchzuführen usw.

Wenn Sie jedoch wirklich in diese Richtung gehen möchten, sollten Sie TestNG in Betracht ziehen, da es das Ausführen von Testmethoden in beliebiger Reihenfolge unterstützt (und Dinge wie die Angabe dieser Methoden hängen von Gruppen von Methoden ab). Cedric Beust erklärt, wie dies in der Reihenfolge der Ausführung von Tests in testng zu tun ist .

Pascal Thivent
quelle
14
Entweder haben Sie zwei unabhängige Tests oder Sie haben nur einen Test und sollten als solcher codieren.
Jon Freedman
2
@ JonFreedman, so wie ich die Frage verstehe, geht es nicht darum, dass die Tests voneinander abhängig sind, sondern nur darum, eine bestimmte Spezifikation zu testen und die Ergebnisse in dieser Reihenfolge anzuzeigen.
Jon Bright
174
Ich kann verstehen, dass die Reihenfolge für Komponententests nicht erzwungen wird. Wenn Sie jedoch JUnit zum Schreiben von Integrationstests verwenden, wäre es schön, die Reihenfolge angeben zu können, in der Tests ausgeführt werden. ZB Führen Sie zuerst den Anmeldetest durch.
Brian DiCasa
13
@BrianD. Login ist wahrscheinlich ein "Fixture" anstelle eines Tests, der vor allen anderen ausgeführt werden muss. Ich werde wahrscheinlich eine BeforeClass schreiben, die sich anmeldet, und dann die Tests schreiben, die in beliebiger Reihenfolge ausgeführt werden sollen.
Marcospereira
47
Die Implikation "Tests sollten unabhängig sein => Tests sollten ORDER-unabhängig sein" ist nicht wahr. Erwägen Sie eine automatisierte Bewertung der Hausaufgaben der Schüler. Ich möchte ihre Lösung zuerst für kleinere Eingaben und später für größere Eingaben testen. Wenn die Lösung für kleinere Eingaben fehlschlägt (für Zeit- / Speicherlimit), warum sollten die Tests dann für größere Eingaben ausgeführt werden?
Mirelon
96

Wenn Sie Ihre vorhandene Junit-Instanz entfernen und JUnit 4.11 oder höher im Erstellungspfad herunterladen , führt der folgende Code die Testmethoden in der Reihenfolge ihrer Namen aus, sortiert in aufsteigender Reihenfolge:

@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");
    }
}
Aniket Kulkarni
quelle
8
Wir haben zum Beispiel tatsächlich eine ähnliche Methode test_001_login () ausprobiert, aber obwohl dies hauptsächlich zur Aufrechterhaltung der Ordnung funktioniert, ist dies nicht garantiert. Wir haben mehrere Instanzen pro Testlauf, in denen 004, 005 und 006 nach 007 ausgeführt werden Sie sagen "WTF!" und laufen zu StackOverflow, um Antworten zu erhalten.
Max P Magee
Genial - verfügbar in JUnit 4.12
DtechNet
1
in meinen Tests: testAcase - funktioniert, test_A_case / testA_case - nicht!
Rodislav Moldovan
6
Ich habe diesen Annotationsparameter "MethodSorters.JVM" ausprobiert, z. B. "@FixMethodOrder (MethodSorters.JVM)". Über die API: JVM - Belässt die Testmethoden in der von der JVM zurückgegebenen Reihenfolge. Funktioniert gut für meine Arbeit (CRUD), führt die Testmethoden in der Reihenfolge aus, in der sie geschrieben sind. +1
Edvinauskas
1
Diese Anmerkung ist in der Tat eine Antwort, hat jedoch die Einschränkung, dass sie nicht definiert ist (in Junit 4.12) @Inheritedund daher für meine AbstractTestCaseElternklasse unwirksam wird .
AbVog
49

Wenn die Bestellung wichtig ist, sollten Sie die Bestellung selbst aufgeben.

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

Insbesondere sollten Sie bei Bedarf einige oder alle möglichen zu testenden Auftragspermutationen auflisten.

Zum Beispiel,

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(); }

Oder ein vollständiger Test aller Permutationen:

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

Hier permute()ist eine einfache Funktion, die alle möglichen Permuationen in eine Sammlung von Arrays iteriert.

Xiè Jìléi
quelle
Aber was ist, wenn Tests in verschiedenen Dateien?
Oleg Abrazhaev
3
Im ersten Codeblock, test2läuft test1 wieder . Junit kann noch test2vorher laufen test1. Dies ist wahrscheinlich nicht das, was Sie beabsichtigt haben, und keine gültige Antwort auf die Frage.
Werkzeugschmied
47

Die Migration zu TestNG scheint der beste Weg zu sein, aber ich sehe hier keine klare Lösung für jUnit. Hier ist die am besten lesbare Lösung / Formatierung, die ich für jUnit gefunden habe:

@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(){}
}

Dies stellt sicher, dass Stage2-Methoden nach Stage1-Methoden und vor Stage3-Methoden aufgerufen werden.

joro
quelle
5
Dieser Ansatz ist nett, aber es wäre gültig zu erwähnen, dass wenn Sie mehr als 10 Tests haben, es nicht gut funktioniert, es sei denn, Sie fügen ein 0Präfix hinzu, zBvoid stage01_prepareAndTest(){ }
EliuX
Wenn Sie mehr als 10 Stufen haben (keine Tests) - Ja. Ich bevorzuge es, die Anzahl der Stufen zu begrenzen und mehr Tests in jeder Stufe durchzuführen, wenn dies möglich ist.
Joro
18

Es ist eines der Hauptprobleme, mit denen ich bei der Arbeit an Junit konfrontiert war, und ich habe folgende Lösung gefunden, die für mich gut funktioniert:

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

Erstellen Sie auch eine Schnittstelle wie unten:

 @Retention(RetentionPolicy.RUNTIME)


@Target({ ElementType.METHOD})

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

Angenommen, Sie haben Klasse A, in der Sie mehrere Testfälle wie die folgenden geschrieben haben:

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

void method(){

//do something

}

}

Die Ausführung beginnt also mit der Methode "method ()". Vielen Dank!

Aman Goel
quelle
Verwenden eines anderen JUnit-Läufers mit PowerMock Seit Version 1.6.0 unterstützt PowerMock das Delegieren der Testausführung an einen anderen JUnit-Läufer ohne Verwendung einer JUnit-Regel. Dadurch bleibt die eigentliche Testausführung einem anderen Läufer Ihrer Wahl überlassen. @RunWith(PowerMockRunner.class) @PowerMockRunnerDelegate(OrderedRunner.class)
Kyriakos Georgiopoulos
11

Mit JUnit können Testmethoden derzeit die Reihenfolge mithilfe von Klassenanmerkungen ausführen:

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

Standardmäßig werden Testmethoden in alphabetischer Reihenfolge ausgeführt. Um die Reihenfolge bestimmter Methoden festzulegen, können Sie sie wie folgt benennen:

a_TestWorkUnit_WithCertainState_ShouldDoSomething b_TestWorkUnit_WithCertainState_ShouldDoSomething c_TestWorkUnit_WithCertainState_ShouldDoSomething

Beispiele finden Sie hier .

Zon
quelle
8

Die (noch nicht veröffentlichte) Änderung https://github.com/junit-team/junit/pull/386 führt a ein @SortMethodsWith. https://github.com/junit-team/junit/pull/293 machte zumindest die Reihenfolge ohne diese vorhersehbar (in Java 7 kann es ziemlich zufällig sein).

Jesse Glick
quelle
1
Scheint, dass # 386 in 4.11 zusammengeführt wurde.
Jesse Glick
Wie weiter unten auf dieser Seite erwähnt, heißt es eigentlich @FixMethodOrdernicht @SortMethodsWithin 4.11
vorburger
6

Sehen Sie sich einen JUnit-Bericht an. JUnit ist bereits nach Paketen organisiert. Jedes Paket hat (oder kann) TestSuite-Klassen, von denen jede wiederum mehrere Testfälle ausführt. Jeder TestCase kann mehrere Testmethoden des Formulars haben public void test*(), von denen jede tatsächlich eine Instanz der TestCase-Klasse wird, zu der sie gehören. Jede Testmethode (TestCase-Instanz) hat einen Namen und ein Bestanden / Nicht Bestanden-Kriterium.

Was mein Management benötigt, ist das Konzept einzelner TestStep- Elemente, von denen jedes seine eigenen Bestanden / Nicht Bestanden- Kriterien angibt . Das Fehlschlagen eines Testschritts darf die Ausführung nachfolgender Testschritte nicht verhindern.

In der Vergangenheit haben Testentwickler in meiner Position TestCase-Klassen in Paketen organisiert, die den Teilen des zu testenden Produkts entsprechen, eine TestCase-Klasse für jeden Test erstellt und jede Testmethode zu einem separaten "Schritt" im Test gemacht. komplett mit eigenen Pass / Fail-Kriterien in der JUnit-Ausgabe. Jeder TestCase ist ein eigenständiger "Test", aber die einzelnen Methoden oder Testschritte innerhalb des TestCase müssen in einer bestimmten Reihenfolge ausgeführt werden.

Die TestCase-Methoden waren die Schritte des TestCase, und Testdesigner erhielten pro Testschritt ein separates Pass / Fail-Kriterium. Jetzt sind die Testschritte durcheinander und die Tests schlagen (natürlich) fehl.

Zum Beispiel:

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()

Jede Testmethode bestätigt und meldet ihre eigenen Pass / Fail-Kriterien. Wenn Sie dies zum Zwecke der Bestellung in "eine große Testmethode" zusammenfassen, wird die Granularität der Pass / Fail-Kriterien für jeden "Schritt" im JUnit-Zusammenfassungsbericht verloren. ... und das ärgert meine Manager. Sie fordern derzeit eine andere Alternative.

Kann jemand erklären, wie eine JUnit mit verschlüsselter Testmethodenreihenfolge separate Pass / Fail-Kriterien für jeden sequentiellen Testschritt unterstützt, wie oben beispielhaft dargestellt und von meinem Management gefordert?

Unabhängig von der Dokumentation sehe ich dies als ernsthafte Regression im JUnit-Framework, die vielen Testentwicklern das Leben schwer macht.

Anonymer Entwickler
quelle
6

JUnit 5 Update (und meiner Meinung nach)

Ich denke, es ist eine ziemlich wichtige Funktion für JUnit. Wenn der Autor von JUnit die Bestellfunktion nicht möchte, warum?

Standardmäßig versuchen Unit-Test-Bibliotheken nicht, Tests in der Reihenfolge auszuführen, die in der Quelldatei vorkommt.
JUnit 5 als JUnit 4 funktionieren auf diese Weise. Warum ? Denn wenn die Reihenfolge wichtig ist, bedeutet dies, dass einige Tests zwischen ihnen gekoppelt sind und dies für Komponententests unerwünscht ist .
Die @Nestedvon JUnit 5 eingeführte Funktion folgt daher demselben Standardansatz.

Bei Integrationstests kann jedoch die Reihenfolge der Testmethode von Bedeutung sein, da eine Testmethode den Status der Anwendung auf eine Weise ändern kann, die von einer anderen Testmethode erwartet wird. Wenn Sie beispielsweise einen Integrationstest für eine E-Shop-Kaufabwicklung schreiben, registriert die erste auszuführende Testmethode die Registrierung eines Kunden, die zweite das Hinzufügen von Artikeln zum Warenkorb und die letzte die Kaufabwicklung. Wenn der Testläufer diese Reihenfolge nicht einhält, ist das Testszenario fehlerhaft und schlägt fehl.
In JUnit 5 (ab Version 5.4) haben Sie dennoch die Möglichkeit, die Ausführungsreihenfolge festzulegen, indem Sie die Testklasse mit Anmerkungen versehen @TestMethodOrder(OrderAnnotation.class)und die Reihenfolge mit @Order(numericOrderValue)für die Methoden angeben, für die die Reihenfolge von Bedeutung ist.

Beispielsweise :

@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");
    }
}

Ausgabe :

createUserAndLogin

addItemsInBasket

checkoutOrder

Die Angabe @TestMethodOrder(OrderAnnotation.class)scheint übrigens nicht erforderlich zu sein (zumindest in der von mir getesteten Version 5.4.0).

Randnotiz
Zur Frage: Ist JUnit 5 die beste Wahl, um Integrationstests zu schreiben? Ich denke nicht, dass dies das erste zu berücksichtigende Tool sein sollte (Cucumber und Co bringen häufig spezifischere Werte und Funktionen für Integrationstests mit), aber in einigen Integrationstestfällen reicht das JUnit-Framework aus. Das ist also eine gute Nachricht, dass die Funktion vorhanden ist.

davidxxx
quelle
4

Ich bin mir nicht sicher, ob ich damit einverstanden bin. Wenn ich "Datei-Upload" und dann "Durch Datei-Upload eingefügte Daten" testen möchte, warum möchte ich dann nicht, dass diese unabhängig voneinander sind? Ich denke, es ist absolut vernünftig, sie separat ausführen zu können, anstatt beide in einem Goliath-Testfall zu haben.

Mattk
quelle
3

Was Sie wollen, ist durchaus sinnvoll, wenn Testfälle als Suite ausgeführt werden.

Leider ist momentan keine Zeit, eine vollständige Lösung zu geben, aber schauen Sie sich die Klasse an:

org.junit.runners.Suite

Damit können Sie Testfälle (aus jeder Testklasse) in einer bestimmten Reihenfolge aufrufen.

Diese können verwendet werden, um Funktions-, Integrations- oder Systemtests zu erstellen.

Dadurch bleiben Ihre Komponententests unverändert (wie empfohlen), unabhängig davon, ob Sie sie so ausführen oder nicht, und Sie können die Tests dann als Teil eines Gesamtbilds wiederverwenden.

Wir verwenden / erben denselben Code für Einheiten-, Integrations- und Systemtests wieder, manchmal datengesteuert, manchmal festschreibungsgesteuert und manchmal als Suite ausgeführt.

CharlieS
quelle
2
Haben Sie seit 2014 keine Zeit mehr gehabt, diese Antwort zu vervollständigen? ;)
Charlie
2

Siehe meine Lösung hier: "Junit und Java 7."

In diesem Artikel beschreibe ich, wie Junit-Tests in der richtigen Reihenfolge ausgeführt werden - "genau wie in Ihrem Quellcode". Tests werden ausgeführt, damit Ihre Testmethoden in der Klassendatei angezeigt werden.

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

Aber wie Pascal Thivent sagte, ist dies keine gute Praxis.

kornero
quelle
Ich hatte Ihren Blog-Beitrag gesehen (auf Russisch!), Aber das ist viel zu kompliziert.
Nicolas Barbulesco
1
@NicolasBarbulesco Ich habe zwei Blogs (rus und eng). Dies ist zu kompliziert, da Sie keine Tests mit Abhängigkeit von der Ausführungsreihenfolge erstellen sollten. Meine Lösung ist eine Problemumgehung, aber eine echte Lösung besteht darin, diese Abhängigkeit zu entfernen.
Kornero
1
Dieser Beitrag enthält keine tatsächliche Antwort. Bitte fügen Sie neben dem Link mindestens die grundlegende Erklärung hinzu.
Standardgebietsschema
0

Ich habe ein paar Antworten gelesen und bin damit einverstanden, dass dies nicht die beste Vorgehensweise ist, aber der einfachste Weg, Ihre Tests zu bestellen - und die Art und Weise, wie JUnit Tests standardmäßig ausführt, ist der aufsteigende alphabetische Name.

Benennen Sie Ihre Tests einfach in der gewünschten alphabetischen Reihenfolge. Beachten Sie auch, dass der Testname mit dem Wort test beginnen muss. Achten Sie einfach auf Zahlen

test12 wird vor test2 ausgeführt

damit:

testA_MyFirstTest testC_ThirdTest testB_ATestThatRunsSecond

pstorli
quelle
0

Bitte überprüfen Sie diese: https://github.com/TransparentMarket/junit . Der Test wird in der angegebenen Reihenfolge ausgeführt (definiert in der kompilierten Klassendatei). Außerdem verfügt es über eine AllTests-Suite, mit der Tests ausgeführt werden können, die zuerst vom Unterpaket definiert werden. Mit der AllTests-Implementierung kann die Lösung erweitert werden, indem auch nach Eigenschaften gefiltert wird (wir haben früher @ Fast-Annotationen verwendet, diese wurden jedoch noch nicht veröffentlicht).

Martin Kersten
quelle
0

Hier ist eine Erweiterung von JUnit, die das gewünschte Verhalten erzeugen kann: https://github.com/aafuks/aaf-junit

Ich weiß, dass dies gegen die Autoren der JUnit-Philosophie verstößt, aber wenn Sie JUnit in Umgebungen verwenden, in denen keine strengen Unit-Tests durchgeführt werden (wie in Java praktiziert), kann dies sehr hilfreich sein.

shlomi33
quelle
0

Ich bin hier gelandet und habe gedacht, dass meine Tests nicht in der richtigen Reihenfolge durchgeführt wurden, aber die Wahrheit ist, dass das Chaos in meinen asynchronen Jobs lag. Wenn Sie mit Parallelität arbeiten, müssen Sie auch Parallelitätsprüfungen zwischen Ihren Tests durchführen. In meinem Fall teilen sich Jobs und Tests ein Semaphor, sodass die nächsten Tests hängen bleiben, bis der ausgeführte Job die Sperre aufhebt.

Ich weiß, dass dies nicht vollständig mit dieser Frage zusammenhängt, könnte aber möglicherweise helfen, das richtige Problem zu finden

McCoy
quelle
0

Sie können einen dieser Codes verwenden:

@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 { ...}
Arezoo Bagherzadi
quelle