Bagaimana menjalankan metode pengujian dalam urutan tertentu di JUnit4?

413

Saya ingin menjalankan metode pengujian yang dijelaskan oleh @Testdalam urutan tertentu.

Sebagai contoh:

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

Saya ingin memastikan untuk berjalan test1()sebelum test2()setiap kali saya berlari MyTest, tetapi saya tidak dapat menemukan anotasi seperti@Test(order=xx) .

Saya pikir ini fitur yang cukup penting untuk JUnit, jika penulis JUnit tidak menginginkan fitur pesanan , mengapa?

卢 声 远 Shengyuan Lu
sumber
2
Mereka bagi saya tampaknya dieksekusi dalam urutan mereka muncul di file sumber.
Marquis of Lorne
84
Anda seharusnya tidak pernah menulis tes yang perlu dijalankan dalam urutan yang ditentukan. Itu praktik yang sangat buruk. Setiap tes harus bisa berjalan mandiri.
Apfelsaft
5
@ EJP ini hampir secara universal benar dari java pre 7. Pra 7, sebagian besar JVM melakukan ini, tetapi tidak pernah dijamin. Java 7 JVMs dapat mengembalikan metode dalam urutan non-deterministik.
Matthew Farwell
17
Bekerja di sekitar. Hapus @Test dari kotak uji Anda, ubah sebagai fungsi pribadi, lalu buat satu kotak uji tunggal, dan panggil fungsi pribadi secara berurutan.
Simon Guo
12
Menghapus @Test dari test case akan mengacaukan laporan JUnit. Ngomong-ngomong, menegakkan urutan tertentu adalah praktik buruk untuk tes Unit tetapi tidak harus merupakan praktik buruk untuk tes Integrasi . Pilihan terbaik (tidak ideal) adalah untuk membubuhi keterangan kelas dengan @FixMethodOrder(MethodSorters.NAME_ASCENDING), menjaga @Testpenjelasan untuk semua metode tes dan mengubah nama mereka abjad tergantung pada urutan yang diinginkan eksekusi, misalnya t1_firstTest(), t2_secondTest(), dll
MisterStrickland

Jawaban:

238

Saya pikir ini fitur yang cukup penting untuk JUnit, jika penulis JUnit tidak menginginkan fitur pesanan, mengapa?

Saya tidak yakin ada cara bersih untuk melakukan ini dengan JUnit, setahu saya JUnit mengasumsikan bahwa semua tes dapat dilakukan dalam urutan acak. Dari FAQ:

Bagaimana cara menggunakan perlengkapan tes?

(...) Pemesanan doa metode-metode tidak dijamin , jadi testOneItemCollection () mungkin dijalankan sebelum testEmptyCollection (). (...)

Kenapa gitu? Yah, saya percaya bahwa membuat tes tergantung pesanan adalah praktik yang tidak ingin dipromosikan oleh penulis. Tes harus independen, tidak boleh digabungkan dan melanggar ini akan membuat hal-hal lebih sulit untuk dipertahankan, akan merusak kemampuan untuk menjalankan tes secara individual (jelas), dll.

Yang sedang berkata, jika Anda benar-benar ingin pergi ke arah ini, pertimbangkan untuk menggunakan TestNG karena mendukung menjalankan metode tes dalam urutan acak (dan hal-hal seperti menentukan bahwa metode tergantung pada kelompok metode). Cedric Beust menjelaskan cara melakukan ini dalam rangka pelaksanaan tes dalam testng .

Thivent Pascal
sumber
14
Entah Anda memiliki dua tes independen, atau Anda hanya memiliki satu tes dan harus kode seperti itu.
Jon Freedman
2
@JonFreedman, seperti yang saya pahami pertanyaannya, ini bukan kasus tes yang saling tergantung, hanya memiliki spesifikasi hal untuk diuji dan ingin hasilnya muncul dalam urutan itu.
Jon Bright
174
Saya dapat memahami bahwa tidak menegakkan pesanan untuk pengujian unit, namun ketika menggunakan JUnit untuk menulis tes integrasi, alangkah baiknya untuk dapat menentukan urutan pengujian yang dijalankan. Misalnya Jalankan tes masuk terlebih dahulu.
Brian DiCasa
13
@BrianD. login mungkin merupakan "fixture" dan bukan tes yang harus dijalankan sebelum yang lainnya. Saya mungkin akan menulis BeforeClass yang masuk dan kemudian menulis tes untuk dieksekusi dalam urutan apa pun.
marcospereira
47
Implikasinya "tes harus independen => tes harus independen ORDER" tidak benar. Pertimbangkan penilaian pekerjaan rumah siswa secara otomatis. Saya ingin menguji solusi mereka untuk input yang lebih kecil terlebih dahulu dan untuk input yang lebih besar nanti. Ketika solusi gagal untuk input yang lebih kecil (untuk batas waktu / memori), lalu mengapa pengujian harus dijalankan untuk input yang lebih besar?
mirelon
96

Jika Anda menyingkirkan instance Junit yang ada, dan mengunduh JUnit 4.11 atau lebih besar di jalur build, kode berikut akan menjalankan metode pengujian dalam urutan nama mereka, diurutkan dalam urutan naik:

@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
sumber
8
Kami sebenarnya telah mencoba metode serupa test_001_login (), misalnya, tetapi meskipun sebagian besar berfungsi untuk menjaga ketertiban, itu tidak dijamin - kami memiliki beberapa contoh per uji coba di mana 004, 005, dan 006 dijalankan setelah 007. Itu membuat Anda mengatakan, "WTF !," dan lari ke StackOverflow untuk mendapatkan jawaban.
Max P Magee
Luar biasa - tersedia di JUnit 4.12
DtechNet
1
dalam tes saya: testAcase - berhasil, test_A_case / testA_case - tidak!
Rodislav Moldovan
6
Saya telah mencoba parameter anotasi ini "MethodSorters.JVM", mis. "@FixMethodOrder (MethodSorters.JVM)". Dari API: JVM - Meninggalkan metode pengujian sesuai urutan yang dikembalikan oleh JVM. Bekerja dengan baik untuk apa yang saya lakukan (CRUD), menjalankan metode pengujian sesuai urutan ditulis. +1
Edvinauskas
1
Anotasi ini memang jawaban, tetapi memiliki peringatan bahwa itu tidak didefinisikan (dalam Junit 4.12) dengan @Inheriteddan karenanya menjadi tidak efektif pada AbstractTestCasekelas orang tua saya .
AbVog
49

Jika pesanan itu penting, Anda harus memesan sendiri.

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

Secara khusus, Anda harus mendaftar beberapa atau semua permutasi pesanan yang mungkin untuk diuji, jika perlu.

Sebagai contoh,

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

Atau, tes lengkap dari semua permutasi:

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

Di sini, permute()adalah fungsi sederhana yang mengubah semua permuasi yang mungkin menjadi Kumpulan array.

Xiè Jìléi
sumber
Tetapi bagaimana jika tes dalam file yang berbeda?
Oleg Abrazhaev
3
Di blok kode pertama, test2jalankan test1 lagi . Junit mungkin masih berjalan test2sebelumnya test1. Ini mungkin bukan yang Anda maksudkan, dan bukan jawaban yang valid untuk pertanyaan itu.
toolforger
47

Migrasi ke TestNG tampaknya cara terbaik, tapi saya tidak melihat solusi yang jelas untuk jUnit. Inilah solusi / pemformatan yang paling mudah dibaca yang saya temukan untuk 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(){}
}

Ini memastikan metode stage2 dipanggil setelah yang stage1 dan sebelum yang stage3.

joro
sumber
5
Pendekatan ini bagus, tetapi akan valid untuk menyebutkan bahwa jika Anda memiliki lebih dari 10 tes itu tidak akan berfungsi dengan baik kecuali jika Anda menambahkan 0awalan, misalnyavoid stage01_prepareAndTest(){ }
EliuX
Jika Anda memiliki lebih dari 10 tahap (bukan tes) - Ya. Saya lebih suka membatasi jumlah tahapan dan memiliki lebih banyak tes di setiap tahap, saat ini memungkinkan.
joro
18

Ini salah satu masalah utama yang saya hadapi ketika saya bekerja pada Junit dan saya datang dengan solusi berikut yang berfungsi dengan baik untuk saya:

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

juga buat antarmuka seperti di bawah ini:

 @Retention(RetentionPolicy.RUNTIME)


@Target({ ElementType.METHOD})

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

Sekarang anggaplah Anda memiliki kelas A di mana Anda telah menulis beberapa test case seperti di bawah ini:

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

void method(){

//do something

}

}

Jadi eksekusi akan dimulai dari metode bernama "method ()". Terima kasih!

Aman Goel
sumber
Menggunakan Runner JUnit lain dengan PowerMock Sejak versi 1.6.0 PowerMock memiliki dukungan untuk mendelegasikan eksekusi tes ke runner JUnit lain tanpa menggunakan JUnit Rule. Ini meninggalkan ujian-eksekusi aktual untuk pelari lain pilihan Anda. @RunWith(PowerMockRunner.class) @PowerMockRunnerDelegate(OrderedRunner.class)
Kyriakos Georgiopoulos
11

JUnit saat ini memungkinkan metode pengujian menjalankan pemesanan menggunakan anotasi kelas:

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

Secara default, metode pengujian dijalankan dalam urutan abjad. Jadi, untuk mengatur urutan metode tertentu, Anda dapat menamainya seperti:

a_TestWorkUnit_WithCertainState_ShouldDoSesuatu b_TestWorkUnit_WithCertainState_ShouldDoSesuatu

Anda dapat menemukan contoh di sini .

Zon
sumber
6

Lihatlah laporan JUnit. JUnit sudah diatur oleh paket. Setiap paket memiliki (atau dapat memiliki) kelas TestSuite, yang masing-masing menjalankan beberapa TestCases. Setiap TestCase dapat memiliki beberapa metode pengujian formulirpublic void test*() , yang masing-masing akan benar-benar menjadi turunan dari kelas TestCase yang menjadi tempatnya. Setiap metode pengujian (contoh TestCase) memiliki nama dan kriteria lulus / gagal.

Apa yang dibutuhkan manajemen saya adalah konsep TestStep individu item , yang masing-masing melaporkan kriteria lulus / gagal sendiri. Kegagalan langkah uji tidak boleh mencegah pelaksanaan langkah uji selanjutnya.

Di masa lalu, pengembang pengujian di posisi saya mengatur kelas TestCase ke dalam paket yang sesuai dengan bagian produk yang diuji, membuat kelas TestCase untuk setiap tes, dan menjadikan setiap metode uji sebagai "langkah" terpisah dalam pengujian, lengkap dengan kriteria lulus / gagal sendiri dalam output JUnit. Setiap TestCase adalah "tes" mandiri, tetapi metode individual, atau uji "langkah" dalam TestCase, harus terjadi dalam urutan tertentu.

Metode TestCase adalah langkah-langkah dari TestCase, dan perancang tes mendapat kriteria lulus / gagal per langkah uji. Sekarang langkah-langkah tes dicampuradukkan, dan tes (tentu saja) gagal.

Sebagai contoh:

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

Setiap metode pengujian menegaskan dan melaporkan sendiri kriteria lulus / gagal. Menjatuhkan ini ke dalam "satu metode uji besar" demi memesan kehilangan rincian lulus / gagal kriteria setiap "langkah" dalam laporan ringkasan JUnit. ... dan itu mengganggu manajer saya. Mereka saat ini menuntut alternatif lain.

Adakah yang bisa menjelaskan bagaimana JUnit dengan pemesanan metode pengujian acak akan mendukung kriteria lulus / gagal terpisah dari setiap langkah uji berurutan, seperti yang dicontohkan di atas dan diperlukan oleh manajemen saya?

Terlepas dari dokumentasi, saya melihat ini sebagai regresi serius dalam kerangka JUnit yang membuat hidup sulit bagi banyak pengembang pengujian.

Pengembang Anonim
sumber
6

Pembaruan 5 JUnit (dan pendapat saya)

Saya pikir ini fitur yang cukup penting untuk JUnit, jika penulis JUnit tidak menginginkan fitur pesanan, mengapa?

Secara default, pustaka pengujian unit tidak mencoba menjalankan tes dalam urutan yang terjadi pada file sumber.
JUnit 5 sebagai JUnit 4 bekerja dengan cara itu. Mengapa Karena jika urutan penting, itu berarti bahwa beberapa tes digabungkan di antara mereka dan itu tidak diinginkan untuk tes unit .
Jadi @Nestedfitur yang diperkenalkan oleh JUnit 5 mengikuti pendekatan standar yang sama.

Tetapi untuk pengujian integrasi, urutan metode pengujian mungkin penting karena metode pengujian dapat mengubah keadaan aplikasi dengan cara yang diharapkan oleh metode pengujian lain. Misalnya ketika Anda menulis tes integrasi untuk pemrosesan checkout e-shop, metode pengujian pertama yang akan dijalankan adalah mendaftarkan klien, yang kedua adalah menambahkan item dalam keranjang dan yang terakhir adalah melakukan checkout. Jika pelari tes tidak menghormati urutan itu, skenario pengujian cacat dan akan gagal.
Jadi dalam JUnit 5 (dari versi 5.4) Anda memiliki semua kemungkinan untuk mengatur urutan eksekusi dengan menjelaskan kelas tes dengan @TestMethodOrder(OrderAnnotation.class)dan dengan menentukan urutan dengan@Order(numericOrderValue) untuk metode-metode yang penting.

Sebagai contoh :

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

Keluaran:

buatUserAndLogin

addItemsInBasket

checkoutOrder

By the way, menentukan @TestMethodOrder(OrderAnnotation.class) sepertinya tidak diperlukan (setidaknya dalam versi 5.4.0 yang saya uji).

Catatan tentang
Tentang pertanyaan: apakah JUnit 5 pilihan terbaik untuk menulis tes integrasi? Saya tidak berpikir bahwa itu harus menjadi alat pertama untuk dipertimbangkan (Mentimun dan rekan mungkin sering membawa nilai dan fitur yang lebih spesifik untuk tes integrasi) tetapi dalam beberapa kasus uji integrasi, kerangka kerja JUnit sudah cukup. Jadi itu adalah kabar baik bahwa fitur tersebut ada.

davidxxx
sumber
4

Tidak yakin saya setuju, Jika saya ingin menguji 'File Upload' dan kemudian menguji 'Data Dimasukkan oleh File Upload' mengapa saya tidak ingin ini independen satu sama lain? Sangat masuk akal saya pikir dapat menjalankannya secara terpisah daripada memiliki keduanya dalam kasus uji Goliath.

Mattk
sumber
3

Apa yang Anda inginkan sangat masuk akal ketika uji kasus dijalankan sebagai suite.

Sayangnya tidak ada waktu untuk memberikan solusi yang lengkap saat ini, tetapi lihatlah di kelas:

org.junit.runners.Suite

Yang memungkinkan Anda untuk memanggil kasus uji (dari kelas tes apa pun) dalam urutan tertentu.

Ini dapat digunakan untuk membuat tes fungsional, integrasi atau sistem.

Ini membuat unit Anda tes karena tanpa urutan tertentu (seperti yang disarankan), apakah Anda menjalankannya seperti itu atau tidak, dan kemudian menggunakan kembali tes sebagai bagian dari gambar yang lebih besar.

Kami menggunakan kembali / mewarisi kode yang sama untuk pengujian unit, integrasi dan sistem, kadang-kadang didorong oleh data, kadang-kadang didorong oleh komit, dan kadang-kadang dijalankan sebagai suite.

CharlieS
sumber
2
Apakah Anda tidak punya waktu untuk menyelesaikan jawaban ini sejak 2014? ;)
Charlie
2

Lihat solusi saya di sini: "Junit dan java 7."

Pada artikel ini saya menjelaskan cara menjalankan tes junit agar - "seperti dalam kode sumber Anda". Tes akan dijalankan, agar metode pengujian Anda muncul di file kelas.

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

Tapi seperti kata Pascal Thivent, ini bukan praktik yang baik.

Kornero
sumber
Saya telah melihat posting blog Anda (dalam bahasa Rusia!), Tapi ini terlalu rumit.
Nicolas Barbulesco
1
@NicolasBarbulesco Saya punya dua blog (rus dan eng). Itu terlalu rumit, karena Anda tidak perlu membuat tes dengan ketergantungan urutan eksekusi. Solusi saya adalah solusi, tetapi solusi nyata - adalah untuk menghapus ketergantungan itu.
kornero
1
Posting ini tidak mengandung jawaban yang sebenarnya. Tolong, pertimbangkan untuk menambahkan setidaknya penjelasan dasar, di samping tautannya.
lokal default
0

Saya telah membaca beberapa jawaban dan setuju itu bukan praktik terbaik, tetapi cara termudah untuk memesan tes Anda - dan cara JUnit menjalankan tes secara default adalah dengan nama naik alfabet.

Jadi cukup beri nama tes Anda dalam urutan abjad yang Anda inginkan. Perhatikan juga nama tes harus dimulai dengan tes kata. Berhati-hatilah dengan angka

test12 akan berjalan sebelum test2

begitu:

testA_MyFirstTest testC_ThirdTest testB_ATestThatRunsSecond

pstorli
sumber
0

Silakan periksa yang ini: https://github.com/TransparentMarket/junit . Itu menjalankan tes dalam urutan yang ditentukan (didefinisikan dalam file kelas yang dikompilasi). Juga fitur suite AllTests untuk menjalankan tes yang ditentukan oleh sub paket terlebih dahulu. Dengan menggunakan implementasi AllTests, seseorang dapat memperluas solusi dengan memfilter properti (kami dulu menggunakan penjelasan @Fast tetapi yang belum dipublikasikan).

Martin Kersten
sumber
0

Berikut ini adalah ekstensi untuk JUnit yang dapat menghasilkan perilaku yang diinginkan: https://github.com/aafuks/aaf-junit

Saya tahu bahwa ini bertentangan dengan penulis filosofi JUnit, tetapi ketika menggunakan JUnit di lingkungan yang tidak ketat dalam pengujian unit (seperti yang dilakukan di Jawa) ini bisa sangat membantu.

shlomi33
sumber
0

Saya berakhir di sini berpikir bahwa tes saya tidak berjalan berurutan, tetapi sebenarnya kekacauan itu ada di pekerjaan async saya. Saat bekerja dengan konkurensi, Anda perlu melakukan cek konkurensi di antara tes Anda juga. Dalam kasus saya, pekerjaan dan tes berbagi semaphore, jadi tes selanjutnya digantung sampai pekerjaan yang berjalan melepaskan kunci.

Saya tahu ini tidak sepenuhnya terkait dengan pertanyaan ini, tetapi mungkin dapat membantu menargetkan masalah yang benar

McCoy
sumber
0

Anda dapat menggunakan salah satu dari potongan kode ini:

@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
sumber