Saya tahu bahwa satu cara untuk melakukannya adalah:
@Test
public void foo(){
try{
//execute code that you expect not to throw Exceptions.
}
catch(Exception e){
fail("Should not have thrown any exception");
}
}
Apakah ada cara yang lebih bersih untuk melakukan ini. (Mungkin menggunakan Junit @Rule
?)
java
unit-testing
junit
exception-handling
junit4
Ankit Dhingra
sumber
sumber
Jawaban:
Anda mendekati ini dengan cara yang salah. Cukup uji fungsionalitas Anda: jika ada pengecualian, pengujian akan gagal secara otomatis. Jika tidak ada pengecualian yang dilemparkan, semua pengujian Anda akan berubah menjadi hijau.
Saya perhatikan pertanyaan ini menarik minat dari waktu ke waktu jadi saya akan sedikit memperluas.
Latar belakang pengujian unit
Ketika Anda menguji unit, penting untuk menentukan sendiri apa yang Anda anggap sebagai unit kerja. Pada dasarnya: ekstraksi basis kode Anda yang mungkin atau mungkin tidak menyertakan beberapa metode atau kelas yang mewakili satu fungsi.
Atau, sebagaimana didefinisikan dalam Seni Pengujian Unit, Edisi ke-2 oleh Roy Osherove , halaman 11:
Apa yang penting untuk disadari adalah bahwa satu unit kerja biasanya bukan hanya satu metode tetapi pada tingkat yang paling dasar itu adalah satu metode dan setelah itu dienkapsulasi oleh unit kerja lain.
Idealnya Anda harus memiliki metode pengujian untuk setiap unit kerja yang terpisah sehingga Anda selalu dapat segera melihat di mana ada masalah. Dalam contoh ini ada metode dasar yang disebut
getUserById()
yang akan mengembalikan pengguna dan ada total 3 unit karya.Unit kerja pertama harus menguji apakah pengguna yang valid dikembalikan dalam kasus input yang valid dan tidak valid.
Setiap pengecualian yang dilemparkan oleh sumber data harus ditangani di sini: jika tidak ada pengguna, harus ada tes yang menunjukkan bahwa pengecualian dilemparkan ketika pengguna tidak dapat ditemukan. Contohnya
IllegalArgumentException
adalah yang tertangkap dengan@Test(expected = IllegalArgumentException.class)
anotasi.Setelah Anda menangani semua usecases untuk unit kerja dasar ini, Anda naik satu level. Di sini Anda melakukan hal yang persis sama, tetapi Anda hanya menangani pengecualian yang berasal dari tingkat tepat di bawah yang sekarang. Ini membuat kode pengujian Anda terstruktur dengan baik dan memungkinkan Anda dengan cepat menelusuri arsitektur untuk menemukan di mana kesalahan terjadi, alih-alih harus melompat ke semua tempat.
Menangani input yang valid dan salah tes
Pada titik ini harus jelas bagaimana kita akan menangani pengecualian ini. Ada 2 jenis input: input yang valid dan input yang salah (input tersebut valid dalam arti yang ketat, tetapi itu tidak benar).
Ketika Anda bekerja dengan input yang valid, Anda menetapkan harapan implisit bahwa tes apa pun yang Anda tulis, akan berhasil.
Seperti metode panggilan dapat terlihat seperti ini:
existingUserById_ShouldReturn_UserObject
. Jika metode ini gagal (misalnya: sebuah pengecualian dilemparkan) maka Anda tahu ada yang tidak beres dan Anda dapat mulai menggali.Dengan menambahkan tes lain (
nonExistingUserById_ShouldThrow_IllegalArgumentException
) yang menggunakan input yang salah dan mengharapkan pengecualian, Anda dapat melihat apakah metode Anda melakukan apa yang seharusnya dilakukan dengan input yang salah.TL; DR
Anda mencoba melakukan dua hal dalam pengujian Anda: periksa input yang valid dan salah. Dengan memecah ini menjadi dua metode yang masing-masing melakukan satu hal, Anda akan memiliki tes yang lebih jelas dan gambaran yang jauh lebih baik tentang di mana kesalahan terjadi.
Dengan tetap mengingat unit kerja yang berlapis-lapis, Anda juga dapat mengurangi jumlah tes yang Anda butuhkan untuk lapisan yang lebih tinggi dalam hierarki karena Anda tidak harus memperhitungkan setiap hal yang mungkin salah di lapisan bawah: lapisan di bawah yang sekarang adalah jaminan virtual bahwa dependensi Anda berfungsi dan jika ada yang salah, itu ada di lapisan Anda saat ini (dengan asumsi lapisan yang lebih rendah tidak melakukan kesalahan sendiri).
sumber
expected
anotasi. Jika Anda ingin menguji skenario di mana kode Anda gagal dan Anda ingin melihat apakah kesalahannya ditangani dengan benar: gunakanexpected
dan mungkin gunakan konfirmasi untuk menentukan apakah itu telah diselesaikan.throws IllegalArgumentException
tes Anda. Yang Anda inginkan pada akhirnya, adalah bahwa tes Anda menjadi merah jika ada pengecualian. Nah, coba tebak? Anda tidak perlu menulisfail()
. Seperti @Jeroen Vannevel menulis: "jika pengecualian dilemparkan tes akan gagal secara otomatis."Saya menemukan ini karena aturan SonarQube "squid: S2699": "Tambahkan setidaknya satu pernyataan untuk kasus uji ini."
Saya menjalani tes sederhana yang satu-satunya tujuan adalah untuk melewati tanpa membuang pengecualian.
Pertimbangkan kode sederhana ini:
Penegasan seperti apa yang dapat ditambahkan untuk menguji metode ini? Tentu, Anda bisa mencoba-coba di sekitarnya, tetapi itu hanya kode mengasapi.
Solusinya datang dari JUnit sendiri.
Jika tidak ada pengecualian yang dilemparkan dan Anda ingin menggambarkan perilaku ini secara eksplisit, cukup tambahkan
expected
seperti dalam contoh berikut:Test.None.class
adalah nilai default untuk nilai yang diharapkan.sumber
Dengan AssertJ pernyataan yang lancar 3.7.0 :
sumber
JUnit 5 (Jupiter) menyediakan tiga fungsi untuk memeriksa tidak adanya / kehadiran pengecualian:
●
assertAll()
Menyatakan bahwa semua yang disediakan
executables
tidak membuang pengecualian.
●
assertDoesNotThrow()
Menyatakan bahwa eksekusi yang
disediakan
executable
/supplier
tidak menimbulkan pengecualian .
Fungsi ini tersedia
sejak JUnit 5.2.0 (29 April 2018).
●
assertThrows()
Menyatakan bahwa eksekusi yang disediakan
executable
melempar pengecualian
expectedType
dan mengembalikan pengecualian .
Contoh
sumber
Java 8 membuat ini jauh lebih mudah, dan Kotlin / Scala jadi dua kali lipat.
Kita dapat menulis kelas utilitas kecil
dan kemudian kode Anda menjadi sederhana:
Jika Anda tidak memiliki akses ke Java-8, saya akan menggunakan fasilitas java lama yang menyakitkan: blok kode aribitrary dan komentar sederhana
Dan akhirnya, dengan kotlin, sebuah bahasa yang saya baru saja jatuh cinta:
Meskipun ada banyak ruang untuk bermain-main dengan persis bagaimana Anda ingin mengekspresikan ini, saya selalu penggemar pernyataan fasih .
Mengenai
Ini pada prinsipnya benar tetapi kesimpulannya salah.
Java memungkinkan pengecualian untuk aliran kontrol. Ini dilakukan oleh runtime JRE sendiri dalam API seperti
Double.parseDouble
viaNumberFormatException
danPaths.get
via aInvalidPathException
.Mengingat Anda telah menulis komponen yang memvalidasi string Number untuk
Double.ParseDouble
, mungkin menggunakan Regex, mungkin parser yang ditulis tangan, atau mungkin sesuatu yang menyematkan beberapa aturan domain lain yang membatasi rentang ganda untuk sesuatu yang spesifik, cara terbaik untuk menguji ini komponen? Saya pikir tes yang jelas akan menyatakan bahwa, ketika string yang dihasilkan diuraikan, tidak ada pengecualian yang dilemparkan. Saya akan menulis tes menggunakan salah satu di atasassertDoesNotThrow
atau/*comment*/{code}
blok. Sesuatu sepertiSaya juga mendorong Anda untuk membuat parameter tes ini saat
input
menggunakanTheories
atauParameterized
agar Anda dapat lebih mudah menggunakan kembali tes ini untuk input lain. Atau, jika Anda ingin menjadi eksotis, Anda bisa menggunakan alat generasi tes (dan ini ). TestNG memiliki dukungan yang lebih baik untuk pengujian parameter.Apa yang menurut saya sangat tidak menyenangkan adalah rekomendasi untuk menggunakan
@Test(expectedException=IllegalArgumentException.class)
, pengecualian ini sangat luas . Jika kode Anda berubah sedemikian rupa sehingga komponen di bawah konstruktor tes memilikiif(constructorArgument <= 0) throw IllegalArgumentException()
, dan tes Anda memasok 0 untuk argumen itu karena nyaman - dan ini sangat umum, karena data uji menghasilkan yang baik adalah masalah yang sangat sulit--, maka tes Anda akan menjadi bilah hijau meskipun tidak menguji apa pun. Tes semacam itu lebih buruk daripada tidak berguna.sumber
Assert.assertThrows
untuk memeriksa bahwa beberapa kode melempar pengecualian.Jika Anda kurang beruntung untuk menangkap semua kesalahan dalam kode Anda. Anda bisa melakukannya dengan bodoh
sumber
Exception ex
harus= null;
sebelum Anda bisa mengujinya.JUnit5 menambahkan metode assertAll () untuk tujuan yang tepat ini.
sumber: JUnit 5 API
sumber
Meskipun pos ini sudah berusia 6 tahun sekarang, namun, banyak yang telah berubah di dunia Junit. Dengan Junit5, sekarang Anda dapat menggunakan
Ex:
Semoga ini akan membantu orang yang menggunakan Junit5 versi terbaru
sumber
Awaitility
'suntilAsserted(ThrowingRunnable assertion)
. Sistem yang sedang diuji saat ini melempar pengecualian khusus pada ThrowingRunnable yang saya berikan, tetapi saya ingin memberikan waktu sampai berhenti melakukannya. Namun jika itu akan melemparkan pengecualian yang berbeda, saya ingin tes gagal secara instan.Jika Anda ingin menguji apakah target pengujian Anda mengkonsumsi pengecualian. Biarkan saja tes sebagai (tiruan kolaborator menggunakan jMock2):
Tes akan berlalu jika target Anda mengkonsumsi pengecualian yang dilemparkan, jika tidak pengujian akan gagal.
Jika Anda ingin menguji logika konsumsi pengecualian Anda, semuanya menjadi lebih kompleks. Saya sarankan mendelegasikan konsumsi ke kolaborator yang bisa diejek. Karena itu tesnya bisa:
Tapi kadang-kadang ini dirancang berlebihan jika Anda hanya ingin mencatatnya. Dalam hal ini, artikel ini ( http://java.dzone.com/articles/monitoring-declarative-transac , http://blog.novoj.net/2008/09/20/testing-aspect-pointcuts-is-there -an-easy-way / ) dapat membantu jika Anda bersikeras tdd dalam kasus ini.
sumber
Gunakan assertNull (...)
sumber
assertNull
juga tidak pernah dieksekusi. Namun pembaca cepat mendapat kesan bahwa pernyataan dibuat yang benar-benar memverifikasi kasus yang tidak melempar. Dengan kata lain: jika blok tangkap tercapai, pengecualian selalu non-nol - dengan demikian dapat diganti dengan sederhanafail
.assertNull(e)
akan melaporkan tes gagal, seperti yang dinyatakane
tidak dapatnull
dicatch
blok ... Mike ini hanya pemrograman aneh: - /. .. ya setidaknya gunakanfail()
seperti kata AndreasAnda dapat berharap bahwa pengecualian tidak dibuang dengan membuat aturan.
sumber
Ini mungkin bukan cara terbaik tetapi pasti memastikan bahwa pengecualian tidak dibuang dari blok kode yang sedang diuji.
sumber
Anda dapat melakukannya dengan menggunakan @Rule dan kemudian memanggil metode reportMissingExceptionWithMessage seperti yang ditunjukkan di bawah ini: Ini adalah kode Scala.
sumber
private val
? Bahasa apakah ini? Jelas bukan Java; p Dan tolong, jangan berikan kode sebagai tangkapan layar, itu tidak disambut.Berikut ini gagal tes untuk semua pengecualian, dicentang atau tidak dicentang:
sumber
Anda dapat membuat segala jenis pernyataan Anda sendiri berdasarkan pernyataan dari junit:
Dan uji:
Secara umum, ada kemungkinan untuk langsung gagal ("bla bla bla") tes dalam skenario apa pun, di tempat mana pun masuk akal. Misalnya menggunakannya dalam blok coba / tangkap untuk gagal jika ada yang dilemparkan ke dalam test case:
Ini adalah contoh dari metode yang kami uji, seandainya kami memiliki metode yang tidak boleh gagal dalam keadaan tertentu, tetapi bisa gagal:
Metode di atas adalah sampel sederhana. Tetapi ini bekerja untuk situasi yang kompleks, di mana kegagalannya tidak begitu jelas. Ada impor:
sumber