Coba tangkap dalam tes JUnit

Saya sedang menulis unit test untuk aplikasi yang sudah ada sejak lama. Beberapa metode yang perlu saya uji dibuat seperti ini:

public void someMethod() throws Exception { 
   //do something 
}

Jika saya ingin menguji metode ini saya harus menulis sesuatu seperti ini di unit test saya:

@Test
public void someTest() {
   try {
      someMethod();
   }
   catch (Exception e) {
      e.printStackTrace();
   }
}

Apakah ini praktik yang baik untuk melakukan ini? Atau adakah cara lain untuk menguji metode ini?

Saya melakukan riset di internet dan menemukan beberapa solusi dengan anotasi @Rule dan @Test(expected=Exception.class), tetapi itu tidak berhasil (Eclipse terus menunjukkan baris someMethod() dalam pengujian sebagai salah). Saya tidak tahu apakah ini solusi yang baik, karena saya cukup baru dalam keseluruhan cerita pengujian unit.

Jika seseorang yang mengetahui banyak tentang hal ini dapat membantu saya, saya akan sangat berterima kasih.


person Nelsch    schedule 15.07.2015    source sumber
comment
Jangan menyembunyikan pengecualian kecuali Anda ingin pengujian tersebut lulus, terlepas dari apakah pengecualian tersebut dilempar atau tidak. Perhatikan bahwa pengujian yang lolos apakah terjadi pengecualian atau tidak mungkin tidak begitu berguna.   -  person Andy Turner    schedule 15.07.2015
comment
Anda bisa membiarkan JUnit menangani Pengecualian dengan menambahkannya ke metode Anda sig: public void someTest() throws Exception. Namun jika Anda ingin menangkap sendiri Pengecualian untuk menegaskannya, contoh yang Anda berikan boleh dilakukan.   -  person s.ijpma    schedule 15.07.2015
comment
@Makoto JUnit menanganinya dengan mencetak jejak tumpukan dan gagal dalam pengujian.   -  person user253751    schedule 15.07.2015
comment
@immibis: Ya, ini benar. Namun dalam konvensi, mendeklarasikan pengecualian yang akan diberikan berarti membiarkan kerangka kerja tingkat yang lebih tinggi menanganinya atau JVM. Seharusnya aku lebih jelas dalam hal itu.   -  person Makoto    schedule 15.07.2015
comment
Apakah itu ide yang bagus tergantung pada apa yang Anda coba lakukan.   -  person Raedwald    schedule 17.07.2015


Jawaban (7)


Karena Exception merupakan pengecualian yang dicentang, Anda juga:

  • Harus menangkap pengecualian dalam pernyataan try...catch, atau
  • Deklarasikan pengecualian yang akan diberikan pada metode itu sendiri.

Apa yang Anda miliki di sana berfungsi dengan baik, tetapi preferensi pribadi saya adalah menyatakan pengecualian yang akan diberikan. Dengan cara ini, jika pengecualian yang tidak saya harapkan muncul saat pengujian dijalankan, pengujian akan gagal.

@Test
public void someTest() throws Exception {
    // dodgy code here
}

Jika kami perlu melihat apakah pengecualian tertentu muncul, maka Anda memiliki opsi untuk menggunakan @Rule atau menambahkan nilai ke anotasi @Test secara langsung.

@Test(expected = FileNotFoundException.class)
public void someTest() throws Exception {
    // dodgy code here
}

Di JUnit 5, Anda dapat memanfaatkan Assertions.assertThrows untuk mencapai hal yang sama. Saya kurang paham dengan ini secara keseluruhan karena ini belum GA pada saat pengeditan, tetapi tampaknya menerima Executable yang berasal dari JUnit 5.

@Test
public void someTest() {
    assertThrows(FileNotFoundException.class, () ->
         { dodgyService.breakableMethod() };
}
person Makoto    schedule 15.07.2015
comment
Faktanya adalah: Saya tidak berharap pengecualian akan diberikan. Bukankah pengujian saya akan gagal jika tidak ada pengecualian yang diberikan? - person Nelsch; 15.07.2015
comment
Tidak! Jika tidak ada pengecualian yang diberikan, pengujian akan lulus secara normal asalkan memenuhi pernyataan Anda yang lain. - person Makoto; 15.07.2015
comment
Oh oke, sepertinya saya kurang memahaminya. Terima kasih! - person Nelsch; 15.07.2015
comment
Ingatlah bahwa hanya karena Anda tidak mengharapkan pengecualian untuk dimasukkan ke dalam sistem produksi Anda, bukan berarti memastikan bahwa pengecualian tersebut dimasukkan dalam pengujian, dan bahwa penanganannya masuk akal, bukanlah ide yang baik. catatan di log, memberi tahu pengguna tentang kegagalan, dll, menutup sistem dengan bersih, menutup thread ~ Anda tidak pernah tahu kapan pemadaman jaringan akan menyebabkan file Anda tidak ditemukan, atau printer tidak tersedia, dll. Jangan biarkan pengguna Anda PC terhenti karena pengecualian yang ditangani dengan buruk menunggu thread dirilis, atau sumber daya tersedia kembali. - person DaveM; 15.07.2015
comment
apa yang Anda miliki di sana berfungsi dengan baik? tidak mungkin, itu sangat buruk. - person Nathan Hughes; 16.07.2015
comment
expected di @Test tidak diperbolehkan mulai JUnit 5. - person Akash Agarwal; 05.08.2017
comment
@AkashAggarwal: Mengingat JUnit5 belum GA, saya sangat tidak menyadari fakta ini. Saya telah merevisi jawaban saya. - person Makoto; 05.08.2017
comment
@Makoto Sama, saya juga tidak menyadarinya. Saya sangat menyukai pendekatan yang diharapkan dan membuang waktu sekitar 10 menit untuk membuatnya berfungsi sampai saya menemukan alasannya. Hanya ingin mencegah orang lain mengalami masalah yang sama. - person Akash Agarwal; 05.08.2017
comment
@AkashAggarwal: ...Anda bisa saja memposting jawaban Anda sendiri, lho... - person Makoto; 05.08.2017
comment
@Makoto Saya merasa ingin berkontribusi pada jawaban dengan suara terbanyak karena lebih mudah dikenali :) - person Akash Agarwal; 05.08.2017

@Test
public void someTest() {
   try {
     someMethod();
   }
   catch (Exception e) {
     Assert.fail("Exception " + e);
   }
}

Adalah apa yang dapat Anda lakukan, jika pengecualian tidak terjadi. Alternatifnya adalah dengan melemparkan pengecualian pada tanda tangan seperti ini:

@Test
public void someTest() throws Exception {
     someMethod();
}

Perbedaannya adalah, dalam satu kasus pengujian akan gagal dengan pengecualian pernyataan dan dalam kasus lain pengujian akan gagal karena pengujian gagal. (seperti di suatu tempat di kode Anda, Anda mendapatkan NPE dan tes akan terjadi karena itu)

Alasan Anda harus melakukan ini adalah karena Exception adalah pengecualian yang dicentang. Lihat Pengecualian yang dicentang versus tidak dicentang

@Test(expected=Exception.class) adalah untuk pengujian, yang ingin menguji apakah pengecualian akan dilempar.

@Test(expected=ArrayIndexOutOfBounds.class)
public void testIndex() {
   int[] array = new int[0];
   int var = array[0]; //exception will be thrown here, but test will be green, because we expect this exception

}
person morpheus05    schedule 15.07.2015
comment
Ini mengimplementasikan ulang cara junit menangani pengecualian, namun lebih buruk lagi, karena Anda kehilangan jejak tumpukan, sehingga sulit untuk mengetahui di mana pengecualian tersebut terjadi. - person Andy Turner; 15.07.2015

Jangan menangkap pengecualian aplikasi Anda dalam kode pengujian Anda. Sebaliknya, nyatakan untuk dilempar ke atas.

Karena, ketika TestRunner JUnit menemukan pengecualian yang dilemparkan, maka secara otomatis akan mencatatnya sebagai error untuk testcase.

Hanya jika Anda testcase mengharapkan metode tersebut menghasilkan Exception Anda harus menggunakan @Test(expected=Exception.class) atau menangkap pengecualian.

Dalam kasus lain, lempar saja ke atas dengan,

public void someTest() throws Exception {
person Codebender    schedule 15.07.2015

Anda dapat menambahkan pengecualian pada tanda tangan metode pengujian. Kemudian, jika Anda menguji apakah pengecualian dilempar, Anda harus menggunakan @Test(expected=Exception.class). Dalam kasus pengujian di mana pengecualian tidak dilempar, pengujian akan berhasil.

@Test
public void testCaseWhereExceptionWontBeThrown() throws Exception {
    someMethod(); //Test pass
}

@Test(expected = Exception.class)
public void testCaseWhereExceptionWillBeThrown() throws Exception {
    someMethod(); //Test pass
}
person Héctor    schedule 15.07.2015
comment
Saya pikir beberapa contoh kode akan membantu jawaban Anda, karena saya pikir dia mungkin sudah mencoba sesuatu yang serupa, tetapi sintaksisnya tidak benar. - person Andy Turner; 15.07.2015
comment
Bagi saya blok kode kedua gagal karena kode yang saya uji memiliki blok coba-tangkap. Saya hanya mencoba menguji apakah pengecualian tertentu terjadi ketika saya memanggil menggunakan nilai buruk. - person shapan dashore; 02.10.2020

Ada dua aturan utama tentang cara memproses pengecualian di penguji Junit:

  1. Jika pengecualian berasal dari kode yang diuji:

    • If it was expected, declare it in the expected attribute of the Test annotation. Or, if further checks should be done on the exception object itself, catch it and ignore it. (In this case, there must be also a call to Assert.fail at the end of the try block, to indicate that the expected exception was not produced).
    • Jika tidak diharapkan, tangkap dan jalankan Assert.fail. (Panggilan sebelumnya ke Exception.printStackTrace juga berguna).
  2. Jika pengecualian tidak berasal dari kode yang diuji atau tidak menarik untuk diuji (misalnya, sebagian besar IOException dihasilkan di tingkat jaringan, bahkan sebelum pengujian dapat diselesaikan), lemparkan kembali pengecualian tersebut pada klausa throws.

Mengapa Anda harus mengharapkan pengecualian pada penguji? Ingatkan: Anda harus mengkodekan satu metode pengujian untuk setiap kemungkinan hasil pada kode yang diuji (untuk mencapai cakupan kode yang tinggi): Dalam kasus Anda, satu metode harus berhasil kembali, dan setidaknya satu metode lainnya yang harus menghasilkan Pengecualian.

person Little Santi    schedule 15.07.2015

Tiga poin tentang JUnit:

  • Pengujian harus tepat, lulus atau gagal dengan jelas hanya berdasarkan pada cara input pengujian disiapkan.

  • Pengujian harus melaporkan kegagalannya kembali ke dalam kerangka kerja.

  • Pengujian tidak boleh bergantung pada pembacaan keluarannya.

Contoh Anda gagal dalam ketiga hal tersebut. Jika pengecualian dilempar atau tidak, pengujian tetap lolos. Jika pengecualian diberikan, JUnit tidak akan pernah mengetahuinya dan tidak dapat memasukkannya ke dalam hasil pengujian. Satu-satunya cara untuk mengetahui ada yang tidak beres adalah dengan membaca apa yang ditulis tes ke stdout, yang membuat kesalahan terlalu mudah untuk diabaikan. Ini bukan cara yang berguna untuk menulis tes.

JUnit dirancang untuk mempermudah melakukan hal yang benar dan memberikan masukan yang berguna bagi pengembang. Jika pengecualian dilempar dari metode pengujian, pengecualian tersebut akan ditangkap oleh kerangka kerja. Jika pengujian dianotasi dengan pengecualian yang menunjukkan bahwa pengecualian memang diharapkan, maka kerangka kerja akan menandai pengujian tersebut sebagai lulus. Jika tidak, kerangka kerja akan gagal dalam pengujian dan mencatat pelacakan tumpukan untuk pelaporan. Kerangka kerja tersebut melaporkan pernyataan apa yang gagal dan pengecualian tak terduga apa yang terjadi sehingga semua orang mengetahui apakah pengujian berhasil atau tidak.

Jika Anda mengharapkan pengujian berhasil tanpa mengeluarkan pengecualian, maka jika ada sesuatu dalam pengujian yang dapat memunculkan pengecualian yang dicentang, tambahkan throws Exception ke tanda tangan metode pengujian. Menambahkan throws ke tanda tangan tidak berarti metode harus membuang apa pun, itu hanya membiarkan pengecualian apa pun yang terjadi dilempar sehingga kerangka pengujian dapat menangkapnya.

Satu-satunya contoh di mana Anda benar-benar menangkap pengecualian dalam pengujian adalah saat Anda ingin menguji pernyataan tentang pengecualian tersebut; misalnya, Anda dapat menguji apakah pesan pada pengecualian sesuai dengan yang Anda harapkan, atau apakah pengecualian tersebut memiliki penyebab tertentu. Dalam hal ini Anda akan menambahkan Assert.fail() di akhir blok percobaan sehingga tidak adanya pengecualian akan menyebabkan pengujian gagal.

Bukan adanya blok coba-tangkap yang buruk, yang buruk adalah tidak adanya apa pun yang akan menyebabkan pengujian gagal.

Saat Anda menulis tes pada awalnya, buatlah tes itu gagal. Dengan begitu Anda membuktikan pada diri sendiri bahwa Anda mengetahui apa yang dilakukan tes tersebut, dan Anda memastikan bahwa, ketika ada kegagalan, Anda akan disadarkan.

person Nathan Hughes    schedule 16.07.2015

Pengecualian macam apa itu? Apakah itu

  1. pengecualian dari melakukan sesuatu seperti menggunakan aliran yang tidak akan terjadi dalam pengujian unit Anda atau
  2. pengecualian yang bisa terjadi karena beberapa masukan yang buruk?

Jika 1. Saya hanya akan meletakkannya di tingkat tanda tangan metode karena try-catch tidak memiliki tujuan nyata selain upacara.

@Test
public void testFoo() throws Exception {
    // ...
}

Jika 2. itu menjadi sedikit lebih rumit. Anda perlu bertanya pada diri sendiri apa yang seharusnya terjadi jika Pengecualian dilempar. Haruskah ujiannya gagal? Apakah itu diharapkan? Apakah itu tidak relevan? Contoh di bawah ini tentang cara menangani semua ini. HATI-HATI: Saya hanya menggunakan Exception karena Anda menggunakannya. Saya berharap hal ini tidak terjadi karena jika ada pengecualian lainnya yang mungkin terjadi selain yang diharapkan maka hal ini akan sangat tidak menguntungkan. Jika memungkinkan jangan gunakan Exception, gunakan yang lebih spesifik (pada kode dan junit).

// The below code assumes you've imported the org.junit.Assert class.

@Test
public void thisShouldFailIfExceptionCaught() {
    //Given...
    try {
        // When...
    } catch (Exception e) {
        Assert.fail();
    }
    // Then...
}

@Test
public void thisShouldPassOnlyIfTheExceptionIsCaught() {
    //Given...
    try {
        // When...
        Assert.fail();
    } catch (Exception expected) {}
    // No "then" needed, the fact that it didn't fail is enough.
}

@Test
public void irrelevantExceptionThatCouldBeThrown() {
    //Given...
    try {
        // When...
    } catch (Exception e) {}
    // Then...
}
person Captain Man    schedule 15.07.2015
comment
Itu tergantung pada metode yang saya gunakan. Bisa berupa JMSException, IOException atau ConfigurationException :) Terima kasih atas jawaban Anda yang mencerahkan! - person Nelsch; 16.07.2015
comment
@Nelsch tidak masalah! Saya akan menyarankan di masa depan bahwa ketika mengajukan pertanyaan untuk tidak menggunakan Exception kecuali Anda benar-benar bersungguh-sungguh (menguji sesuatu yang throws Exception misalnya), bahkan nama seperti SomeCheckedException atau SomeUncheckedException benar-benar menjelaskan masalah yang dihadapi. - person Captain Man; 16.07.2015
comment
Pengujian unit tidak boleh menangkap pengecualian hanya untuk memanggil fail(). Faktanya, semua pengujian unit boleh saja mendeklarasikan Exception karena metode pengujian (seharusnya!) tidak pernah dipanggil dari mana pun kecuali kerangka pengujian. Dan yang terakhir hanya membiarkan pengujian gagal jika ada pengecualian yang Anda inginkan. - person Michel Jung; 04.05.2017