Jak uruchamiać metody testowe w określonej kolejności w JUnit4?

413

Chcę wykonać metody testowe opatrzone adnotacjami @Testw określonej kolejności.

Na przykład:

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

Chcę mieć pewność, że uruchomię się test1()przed test2()każdym uruchomieniem MyTest, ale nie mogłem znaleźć adnotacji takiej jak @Test(order=xx).

Myślę, że jest to dość ważna funkcja dla JUnit, jeśli autor JUnit nie chce funkcji zamawiania , dlaczego?

远 声 远 Shengyuan Lu
źródło
2
Wydaje mi się, że są wykonywane w kolejności, w jakiej występują w pliku źródłowym.
Markiz Lorne
84
Nigdy nie należy pisać testów, które należy wykonać w określonej kolejności. To naprawdę zła praktyka. Każdy test powinien być w stanie działać niezależnie.
Apfelsaft
5
@EJP było to prawie powszechnie prawdziwe w java pre 7. Przed 7, większość JVM to zrobiła, ale nigdy nie było to zagwarantowane. JVM Java 7 może zwracać metody w niedeterministycznej kolejności.
Matthew Farwell
17
Obejść. Usuń @ test ze swoich przypadków testowych, przekonwertuj je jako funkcje prywatne, a następnie utwórz skrzynkę pojedynczą i przetestuj funkcje prywatne w kolejności.
Simon Guo
12
Usunięcie @Test z przypadków testowych zepsuje raport JUnit. Nawiasem mówiąc, egzekwowanie określonej kolejności jest złą praktyką dla testów jednostkowych, ale niekoniecznie złą praktyką dla testów integracyjnych . Najlepszym wyborem (nie idealne) jest opisywanie klasę z @FixMethodOrder(MethodSorters.NAME_ASCENDING), zachować @Testadnotacji dla wszystkich metod badawczych i ich nazwy alfabetycznie zależności od pożądanej kolejności wykonywania, np t1_firstTest(), t2_secondTest()itp
MisterStrickland

Odpowiedzi:

238

Myślę, że jest to dość ważna funkcja dla JUnit, jeśli autor JUnit nie chce funkcji zamawiania, dlaczego?

Nie jestem pewien, czy istnieje czysty sposób, aby to zrobić za pomocą JUnit, o ile wiem, JUnit zakłada, że ​​wszystkie testy można wykonać w dowolnej kolejności. Z FAQ:

Jak korzystać z urządzenia testowego?

(...) Kolejność wywołań metod testowych nie jest gwarantowana , więc testOneItemCollection () może zostać wykonany przed testEmptyCollection (). (...)

Dlaczego tak jest Uważam, że uzależnienie kolejności testów jest praktyką, której autorzy nie chcą promować. Testy powinny być niezależne, nie powinny być łączone, a naruszenie tego sprawi, że będzie trudniej utrzymać, przerwie możliwość uruchamiania testów indywidualnie (oczywiście) itp.

Biorąc to pod uwagę, jeśli naprawdę chcesz iść w tym kierunku, rozważ użycie TestNG, ponieważ obsługuje on natywnie uruchamianie metod testowych w dowolnej dowolnej kolejności (a takie rzeczy, jak określenie, że metody zależą od grup metod). Cedric Beust wyjaśnia, jak to zrobić , wykonując testy w teście .

Pascal Thivent
źródło
14
Albo masz dwa niezależne testy, albo masz tylko jeden test i jako taki powinieneś kodować.
Jon Freedman,
2
@JonFreedman, jak rozumiem pytanie, nie chodzi o to, że testy są współzależne, po prostu o specyfikację rzeczy do przetestowania i o to, żeby wyniki pojawiały się w tej kolejności.
Jon Bright
174
Rozumiem, że nie wymuszam kolejności testów jednostkowych, jednak używając JUnit do pisania testów integracyjnych, fajnie byłoby móc określić kolejność uruchamiania testów. Np. Najpierw uruchom test logowania.
Brian DiCasa
13
@BrianD. login jest prawdopodobnie „urządzeniem” zamiast testu, który musi zostać uruchomiony przed wszystkimi innymi. Prawdopodobnie napiszę klasę BeforeClass, która się zaloguje, a następnie napiszę testy do wykonania w dowolnej kolejności.
marcospereira
47
Implikacja „testy powinny być niezależne => testy powinny być niezależne od ZAMÓWIENIA” nie jest prawdą. Rozważ automatyczne ocenianie zadań domowych uczniów. Chcę najpierw przetestować ich rozwiązanie dla mniejszych danych wejściowych, a dla większych danych później. Kiedy rozwiązanie zawiedzie w przypadku mniejszych danych wejściowych (limit czasu / pamięci), to dlaczego testy powinny działać w przypadku większych danych wejściowych?
mirelon,
96

Jeśli pozbędziesz się istniejącej instancji Junit i pobierzesz JUnit 4.11 lub nowszy ze ścieżki kompilacji, następujący kod wykona metody testowe w kolejności ich nazw, posortowane w kolejności rosnącej:

@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
źródło
8
Wypróbowaliśmy na przykład podobną metodę test_001_login (), ale chociaż działa ona głównie w celu zachowania porządku, nie jest to gwarantowane - mamy kilka wystąpień na uruchomienie testowe, w których 004, 005 i 006 są uruchamiane po 007. To sprawia, że mówisz „WTF!” i biegniesz do StackOverflow w celu uzyskania odpowiedzi.
Max P Magee
Niesamowite - dostępne w JUnit 4.12
DtechNet
1
w moich testach: testAcase - pracował, test_A_case / testA_case - nie!
Rodislav Moldovan
6
Próbowałem tego parametru adnotacji „MethodSorters.JVM”, np. „@FixMethodOrder (MethodSorters.JVM)”. Z interfejsu API: JVM - pozostawia metody testowe w kolejności zwróconej przez JVM. Działa dobrze dla mojego tego, co robię (CRUD), uruchamia metody testowe w kolejności, w jakiej zostały zapisane. +1
Edvinauskas
1
Ta adnotacja jest rzeczywiście odpowiedzią, ale ma zastrzeżenie, że nie jest zdefiniowana (w Junit 4.12) @Inheritedi dlatego staje się nieskuteczna w mojej AbstractTestCaseklasie nadrzędnej.
AbVog,
49

Jeśli zamówienie jest ważne, należy je złożyć samodzielnie.

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

W szczególności powinieneś wymienić niektóre lub wszystkie możliwe kombinacje zleceń do przetestowania, jeśli to konieczne.

Na przykład,

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

Lub pełny test wszystkich permutacji:

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

Tutaj permute()jest prosta funkcja, która iteruje wszystkie możliwe permuacje w zbiorze tablic.

Xiè Jìléi
źródło
Ale co jeśli testy w różnych plikach?
Oleg Abrazhaev
3
W pierwszym bloku kodu test2uruchamia się test1 ponownie . Junit może nadal działać test2wcześniej test1. Prawdopodobnie nie jest to zamierzenie, które nie jest prawidłową odpowiedzią na pytanie.
toolforger
47

Migracja do TestNG wydaje się najlepszym sposobem, ale nie widzę tutaj jasnego rozwiązania dla jUnit. Oto najbardziej czytelne rozwiązanie / formatowanie , które znalazłem dla 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(){}
}

Dzięki temu metody stage2 są wywoływane po metodach stage1 i przed stage3.

joro
źródło
5
Takie podejście jest fajne, ale warto wspomnieć, że jeśli masz więcej niż 10 testów, nie zadziała to dobrze, chyba że dodasz 0prefiks, np.void stage01_prepareAndTest(){ }
EliuX
Jeśli masz więcej niż 10 etapów (nie testy) - Tak. Wolę ograniczać liczbę etapów i mieć więcej testów na każdym etapie, gdy jest to możliwe.
joro
18

Jest to jeden z głównych problemów, z którymi musiałem się zmierzyć, pracując nad Junit i wymyśliłem następujące rozwiązanie, które działa dobrze dla mnie:

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

utwórz również interfejs jak poniżej:

 @Retention(RetentionPolicy.RUNTIME)


@Target({ ElementType.METHOD})

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

Załóżmy teraz, że masz klasę A, w której napisałeś kilka przypadków testowych, takich jak poniżej:

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

void method(){

//do something

}

}

Wykonanie rozpocznie się od metody o nazwie „method ()”. Dzięki!

Aman Goel
źródło
Używanie innego JUnit Runnera z PowerMock Od wersji 1.6.0 PowerMock obsługuje delegowanie wykonania testu do innego JUnit Runnera bez użycia reguły JUnit. To pozostawia rzeczywiste wykonanie testu innemu wybranemu biegaczowi. @RunWith(PowerMockRunner.class) @PowerMockRunnerDelegate(OrderedRunner.class)
Kyriakos Georgiopoulos
11

JUnit obecnie zezwala na kolejność uruchamiania metod testowych przy użyciu adnotacji klas:

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

Domyślnie metody testowe są uruchamiane w kolejności alfabetycznej. Aby ustawić kolejność metod, możesz nazwać je w następujący sposób:

a_TestWorkUnit_WithCertainState_ShouldDoSomething b_TestWorkUnit_WithCertainState_ShouldDoSomething c_TestWorkUnit_WithCertainState_ShouldDoSomething

Przykłady można znaleźć tutaj .

Strefa
źródło
6

Spójrz na raport JUnit. JUnit jest już zorganizowany według pakietów. Każdy pakiet ma (lub może mieć) klasy TestSuite, z których każda z kolei uruchamia wiele TestCases. Każda TestCase może mieć wiele metod testowych formularza public void test*(), z których każda faktycznie stanie się instancją klasy TestCase, do której należą. Każda metoda testowa (instancja TestCase) ma nazwę i kryteria zaliczenia / niepowodzenia.

To, czego wymaga moje zarządzanie, to koncepcja poszczególnych elementów TestStep , z których każdy zgłasza własne kryteria zaliczenia / niepowodzenia. Niepowodzenie jakiegokolwiek kroku testowego nie może uniemożliwić wykonania kolejnych kroków testowych.

W przeszłości programiści testów na moim stanowisku organizowali klasy TestCase w pakiety odpowiadające częściom testowanego produktu, tworzyli klasę TestCase dla każdego testu i uczynili każdą metodę testową osobnym „krokiem” w teście, wraz z własnymi kryteriami pozytywnego / negatywnego wyjścia JUnit. Każda TestCase jest samodzielnym „testem”, ale poszczególne metody lub „kroki” testowe w ramach TestCase muszą występować w określonej kolejności.

Metody TestCase były etapami TestCase, a projektanci testów otrzymali osobne kryterium zaliczenia / niepowodzenia na krok testu. Teraz kroki testowe są pomieszane, a testy (oczywiście) kończą się niepowodzeniem.

Na przykład:

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

Każda metoda testowa potwierdza i zgłasza swoje własne kryteria pozytywnego / negatywnego wyniku. Zebranie tego w „jedną wielką metodę testową” w celu zamówienia traci szczegółowość kryteriów pozytywnych / negatywnych każdego „kroku” w raporcie podsumowującym JUnit. ... i to denerwuje moich menedżerów. Obecnie domagają się innej alternatywy.

Czy ktokolwiek może wyjaśnić, w jaki sposób JUnit z kodowaniem kodowanym metodą testową obsługiwałby osobne kryteria zaliczenia / niepowodzenia dla każdego kolejnego etapu testu, jak pokazano powyżej i jest wymagane przez moje kierownictwo?

Niezależnie od dokumentacji uważam to za poważną regresję w środowisku JUnit, która utrudnia życie wielu programistom testów.

Anonimowy programista
źródło
6

Aktualizacja JUnit 5 (i moja opinia)

Myślę, że jest to dość ważna funkcja dla JUnit, jeśli autor JUnit nie chce funkcji zamawiania, dlaczego?

Domyślnie biblioteki testów jednostkowych nie próbują wykonywać testów w kolejności występującej w pliku źródłowym.
JUnit 5 jako JUnit 4 działają w ten sposób. Dlaczego ? Ponieważ jeśli kolejność ma znaczenie, oznacza to, że niektóre testy są między nimi połączone i jest to niepożądane w przypadku testów jednostkowych .
Tak więc @Nestedfunkcja wprowadzona przez JUnit 5 stosuje to samo domyślne podejście.

Jednak w przypadku testów integracyjnych kolejność metody testowej może mieć znaczenie, ponieważ metoda testowa może zmienić stan aplikacji w sposób oczekiwany przez inną metodę testową. Na przykład, gdy piszesz test integracji przetwarzania transakcji w sklepie internetowym, pierwszą metodą testową do wykonania jest rejestracja klienta, druga to dodawanie produktów do koszyka, a ostatnia to kasy. Jeśli testujący nie przestrzega tej kolejności, scenariusz testowy jest wadliwy i zakończy się niepowodzeniem.
Tak więc w JUnit 5 (od wersji 5.4) masz taką samą możliwość ustawienia kolejności wykonywania poprzez oznaczenie klasy testowej za pomocą @TestMethodOrder(OrderAnnotation.class)i poprzez określenie kolejności @Order(numericOrderValue)dla metod, dla których kolejność ma znaczenie.

Na przykład :

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

Wynik :

createUserAndLogin

addItemsInBasket

Zamówienie

Nawiasem mówiąc, określenie @TestMethodOrder(OrderAnnotation.class)wygląda jak niepotrzebne (przynajmniej w wersji 5.4.0, którą testowałem).

Uwaga dodatkowa
Na pytanie: czy JUnit 5 jest najlepszym wyborem do pisania testów integracyjnych? Nie sądzę, że powinno to być pierwsze narzędzie do rozważenia (Ogórek i spółka mogą często przynosić bardziej szczegółowe wartości i funkcje do testów integracji), ale w niektórych przypadkach testów integracji wystarczy środowisko JUnit. To dobra wiadomość, że ta funkcja istnieje.

davidxxx
źródło
4

Nie jestem pewien, czy zgadzam się. Jeśli chcę przetestować „Przesyłanie pliku”, a następnie przetestować „Dane wstawione przez przesyłanie pliku”, dlaczego nie chcę, aby były one od siebie niezależne? Całkowicie uzasadnione, myślę, że mogę je uruchomić osobno, zamiast mieć oba w przypadku testu Goliata.

Mattk
źródło
3

To, czego chcesz, jest całkowicie uzasadnione, gdy testy są uruchamiane jako pakiet.

Niestety nie ma czasu na podanie kompletnego rozwiązania, ale spójrz na klasę:

org.junit.runners.Suite

Pozwala to wywoływać przypadki testowe (z dowolnej klasy testów) w określonej kolejności.

Można ich użyć do tworzenia testów funkcjonalnych, integracyjnych lub systemowych.

To pozostawia testy jednostkowe bez określonej kolejności (zgodnie z zaleceniami), niezależnie od tego, czy przeprowadzasz je w ten sposób, czy nie, a następnie ponownie wykorzystujesz testy jako część większego obrazu.

Ponownie używamy / dziedziczymy ten sam kod do testów jednostkowych, integracyjnych i systemowych, czasem na podstawie danych, czasem na podstawie zatwierdzeń, a czasem jako pakiet.

CharlieS
źródło
2
Czy nie miałeś czasu na wypełnienie tej odpowiedzi od 2014 r.? ;)
Charlie,
2

Zobacz moje rozwiązanie tutaj: „Junit and java 7.”

W tym artykule opisuję, jak uruchamiać testy junit w kolejności - „tak jak w kodzie źródłowym”. Testy zostaną uruchomione w kolejności, w jakiej metody testowe pojawią się w pliku klasy.

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

Ale jak powiedział Pascal Thivent, nie jest to dobra praktyka.

kornero
źródło
Widziałem twój post na blogu (po rosyjsku!), Ale jest to zbyt skomplikowane.
Nicolas Barbulesco
1
@NicolasBarbulesco Mam dwa blogi (rus i eng). Jest to zbyt skomplikowane, ponieważ nie należy tworzyć testów z zależnością od kolejności wykonywania. Moje rozwiązanie jest obejściem, ale prawdziwym rozwiązaniem jest usunięcie tej zależności.
kornero
1
Ten post nie zawiera rzeczywistej odpowiedzi. Proszę rozważyć dodanie przynajmniej podstawowego objaśnienia oprócz linku.
domyślne ustawienia regionalne
0

Przeczytałem kilka odpowiedzi i zgadzam się, że nie jest to najlepsza praktyka, ale najprostszym sposobem na zamówienie testów - a domyślnym sposobem, w jaki JUnit uruchamia testy, jest alfabetycznie rosnąca nazwa.

Więc nazwij swoje testy w kolejności alfabetycznej, którą chcesz. Pamiętaj też, że nazwa testu musi zaczynać się od słowa test. Uważaj na liczby

test12 uruchomi się przed test2

więc:

testA_MyFirstTest testC_ThirdTest testB_ATestThatRunsSecond

pstorli
źródło
0

Sprawdź to: https://github.com/TransparentMarket/junit . Uruchamia test w kolejności, w jakiej zostały określone (zdefiniowane w skompilowanym pliku klasy). Ponadto zawiera pakiet AllTests do uruchamiania testów zdefiniowanych przez pakiet podrzędny. Korzystając z implementacji AllTests, można rozszerzyć to rozwiązanie również o filtrowanie właściwości (kiedyś używaliśmy adnotacji @Fast, ale nie zostały one jeszcze opublikowane).

Martin Kersten
źródło
0

Oto rozszerzenie JUnit, które może zapewnić pożądane zachowanie: https://github.com/aafuks/aaf-junit

Wiem, że jest to sprzeczne z autorami filozofii JUnit, ale podczas korzystania z JUnit w środowiskach, które nie są rygorystycznymi testami jednostkowymi (jak praktykowane w Javie), może to być bardzo pomocne.

shlomi33
źródło
0

Skończyłem tutaj, myśląc, że moje testy nie zostały przeprowadzone w porządku, ale prawda jest taka, że ​​bałagan był w moich asynchronicznych zadaniach. Podczas pracy z współbieżnością należy również przeprowadzać kontrole współbieżności między testami. W moim przypadku zadania i testy mają wspólny semafor, więc następne testy są zawieszane, dopóki uruchomione zadanie nie zwolni blokady.

Wiem, że nie jest to w pełni związane z tym pytaniem, ale może pomóc w rozwiązaniu właściwego problemu

McCoy
źródło
0

możesz użyć jednego z następujących kodów:

@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
źródło