Bagaimana saya bisa menggunakan JUnit4 ideomatik untuk menguji bahwa beberapa kode melempar pengecualian?
Saat saya pasti bisa melakukan sesuatu seperti ini:
@Test
public void testFooThrowsIndexOutOfBoundsException() {
boolean thrown = false;
try {
foo.doStuff();
} catch (IndexOutOfBoundsException e) {
thrown = true;
}
assertTrue(thrown);
}
Saya ingat bahwa ada sebuah penjelasan atau Menegaskan.xyz atau sesuatu itu jauh lebih kludgy dan jauh lebih in-the-spirit of JUnit untuk jenis situasi.
JUnit 4
memiliki dukungan untuk ini:
@Test(expected = IndexOutOfBoundsException.class)
public void testIndexOutOfBoundsException() {
ArrayList emptyList = new ArrayList();
Object o = emptyList.get(0);
}
Referensi :
Edit Sekarang yang JUnit5 telah dirilis, pilihan terbaik akan menggunakan Pernyataan.assertThrows()
(lihat saya yang lain menjawab).
Jika anda belum't bermigrasi ke JUnit 5, tetapi dapat menggunakan JUnit 4.7, anda dapat menggunakan ExpectedException
Aturan:
public class FooTest {
@Rule
public final ExpectedException exception = ExpectedException.none();
@Test
public void doStuffThrowsIndexOutOfBoundsException() {
Foo foo = new Foo();
exception.expect(IndexOutOfBoundsException.class);
foo.doStuff();
}
}
Ini jauh lebih baik daripada @Test(expected=IndexOutOfBoundsException.class)
karena tes akan gagal jika IndexOutOfBoundsException
dilemparkan sebelum foo.doStuff()
Lihat artikel ini untuk rincian
Hati-hati menggunakan diharapkan pengecualian, karena itu hanya menegaskan bahwa ** metode melemparkan pengecualian, bukan baris tertentu dari kode** dalam tes.
Saya cenderung untuk menggunakan ini untuk pengujian parameter validasi, karena metode tersebut biasanya sangat sederhana, tetapi tes yang lebih kompleks mungkin lebih baik dilayani dengan:
try {
methodThatShouldThrow();
fail( "My method didn't throw when I expected it to" );
} catch (MyException expectedException) {
}
Menerapkan penilaian.
Sebagai dijawab sebelumnya, ada banyak cara untuk berurusan dengan pengecualian di JUnit. Tapi dengan Jawa 8 lain-lain: menggunakan Ekspresi Lambda. Dengan Ekspresi Lambda kita dapat mencapai sintaks seperti ini:
@Test
public void verifiesTypeAndMessage() {
assertThrown(new DummyService()::someMethod)
.isInstanceOf(RuntimeException.class)
.hasMessage("Runtime exception occurred")
.hasMessageStartingWith("Runtime")
.hasMessageEndingWith("occurred")
.hasMessageContaining("exception")
.hasNoCause();
}
assertThrown menerima fungsional antarmuka, dan contoh-contoh yang dapat dibuat dengan ekspresi lambda, metode referensi, atau konstruktor referensi. assertThrown menerima bahwa antarmuka akan mengharapkan dan siap untuk menangani pengecualian.
Ini adalah relatif sederhana namun teknik yang kuat.
Lihatlah posting blog ini menggambarkan teknik ini: http://blog.codeleak.pl/2014/07/junit-testing-exception-with-java-8-and-lambda-expressions.html
Kode sumber dapat ditemukan di sini: https://github.com/kolorobot/unit-testing-demo/tree/master/src/test/java/com/github/kolorobot/exceptions/java8
Disclosure: saya penulis blog dan proyek.
di junit, ada empat cara untuk menguji terkecuali.
assertThrows
sebagai berikut@Tes public void testFooThrowsIndexOutOfBoundsException() { Throwable terkecuali = assertThrows(IndexOutOfBoundsException.class, () -> foo.doStuff()); assertEquals("diharapkan pesan", pengecualian.getMessage()); }
@Test(diharapkan = IndexOutOfBoundsException.class) public void testFooThrowsIndexOutOfBoundsException() { foo.doStuff(); }
public class XxxTest { @Aturan publik ExpectedException dilemparkan = ExpectedException.tidak ada();
@Tes public void testFooThrowsIndexOutOfBoundsException() { dilemparkan.berharap(IndexOutOfBoundsException.class) //anda dapat menguji pengecualian pesan seperti dilemparkan.expectMessage("diharapkan pesan"); foo.doStuff(); } }
@Tes public void testFooThrowsIndexOutOfBoundsException() { try { foo.doStuff(); gagal("diharapkan pengecualian itu tidak terjadi."); } catch(IndexOutOfBoundsException e) { //jika eksekusi mencapai di sini, //hal ini menunjukkan pengecualian ini terjadi. //jadi kita tidak perlu menangani hal itu. } }
jadi
jika anda suka junit 5, maka anda harus seperti 1 satu
2 cara ini digunakan ketika anda hanya ingin menguji jenis pengecualian
yang pertama dan terakhir dua digunakan ketika anda ingin menguji pengecualian pesan selanjutnya
jika anda menggunakan junit 3, kemudian 4 suka
untuk info lebih lanjut, anda dapat membaca dokumen dan junit5 panduan pengguna untuk rincian.
tl;dr
coba-coba
-menangkap
blok. (Don't lupa untuk menambahkan gagal()
pernyataan sebelum menangkap
block) coba-coba
-menangkap
block atau menggunakan JUnit alat (@Test(diharapkan = ...)
atau @Aturan ExpectedException
JUnit aturan feature).
Tapi cara ini tidak begitu elegan dan don't aduk rata readability wise dengan alat-alat lain. Selain itu JUnit perkakas memiliki beberapa perangkap. Coba
-menangkap
blok anda harus menulis blok di sekitar diuji perilaku, dan menulis pernyataan dalam menangkap blok, yang mungkin baik-baik saja tapi banyak menemukan bahwa ini gaya interupsi pembacaan aliran dari suatu tes. Juga anda perlu untuk menulis sebuah Menegaskan.gagal
di akhir mencoba
blok jika tes dapat melewatkan satu sisi pernyataan ; PMD, findbugs atau Sonar akan melihat isu-isu tersebut. @Test(diharapkan = ...)
fitur menarik sebagai anda dapat menulis kode kurang dan kemudian menulis tes ini seharusnya kurang rentan terhadap kesalahan coding. Tapi ths pendekatan yang kurang beberapa daerah. // panggil untuk menjadi benar-benar diuji
diuji.call2(); // call yang seharusnya untuk menaikkan pengecualian
}
3. The ExpectedException
aturan ini juga merupakan upaya untuk memperbaiki peringatan sebelumnya, tapi rasanya sedikit canggung untuk digunakan karena menggunakan sebuah harapan style, EasyMock pengguna tahu betul gaya ini. Mungkin mudah bagi sebagian orang, tetapi jika anda mengikuti Behaviour Didorong Development (BDD) atau Arrange Bertindak Assert (AAA) prinsip-prinsip ExpectedException
aturan tidak't cocok dalam gaya penulisan. Selain itu mungkin menderita masalah yang sama sebagai sebagai @Test
cara, tergantung di mana anda menempatkan harapan.
@Aturan ExpectedException dilemparkan = ExpectedException.tidak ada()
@Tes
public void call2_harus_membuang_a_WantedException__tidak_call1() {
// harapan
dilemparkan.berharap(WantedException.class);
dilemparkan.expectMessage("boom");
// init diuji
diuji.call1(); // dapat membuang WantedException
// panggil untuk menjadi benar-benar diuji
diuji.call2(); // call yang seharusnya untuk menaikkan pengecualian
}
Bahkan diharapkan terkecuali ditempatkan sebelum tes pernyataan, istirahat anda membaca aliran jika tes mengikuti BDD atau AAA.
Juga melihat ini komentar masalah pada JUnit penulis ExpectedException
. JUnit 4.13-beta-2 bahkan deprecates mekanisme ini:
permintaan Tarik #1519: Mencela ExpectedException
metode Menegaskan.assertThrows menyediakan cara yang lebih baik untuk memverifikasi pengecualian. Selain itu penggunaan ExpectedException rentan kesalahan ketika digunakan dengan aturan lain seperti TestWatcher karena urutan dari aturan-aturan penting dalam kasus itu. Jadi ini pilihan di atas memiliki semua beban mereka peringatan, dan jelas tidak kebal terhadap coder kesalahan. 4. Ada's sebuah proyek saya menjadi sadar setelah membuat jawaban ini yang terlihat menjanjikan,'s menangkap pengecualian. Deskripsi dari proyek ini mengatakan, ia membiarkan seorang coder menulis dalam berbahasa baris kode mengejar pengecualian dan menawarkan pengecualian ini untuk kemudian pernyataan. Dan anda dapat menggunakan pernyataan perpustakaan seperti Hamcrest atau AssertJ.
Cepat contoh yang diambil dari halaman rumah :
// yang diberikan: kosong daftar Daftar myList = new ArrayList();
// ketika: kami mencoba untuk mendapatkan elemen pertama dari daftar ketika(myList).mendapatkan(1);
// maka: kami mengharapkan IndexOutOfBoundsException kemudian(caughtException()) .isInstanceOf(IndexOutOfBoundsException.class) .hasMessage("Index: 1, Ukuran: 0") .hasNoCause();
Seperti yang anda lihat kode ini benar-benar sederhana, anda menangkap pengecualian pada baris tertentu, kemudian
API alias yang akan menggunakan AssertJ Api (mirip dengan menggunakan assertThat(ex).hasNoCause()...
). At beberapa titik proyek bergantung pada FEST-Menyatakan nenek moyang AssertJ. EDIT: tampaknya proyek ini adalah pembuatan bir Jawa 8 Lambdas dukungan.
Saat ini perpustakaan ini memiliki dua kelemahan :
inline-mengejek-maker
), sesuatu yang tidak mungkin apa yang anda inginkan, karena hal ini mockmaker memiliki berbagai kelemahan yang biasa mockmaker. Masalah ini tidak't berlaku setelah perpustakaan akan mendukung lambdas, namun fungsi tersebut akan digandakan oleh AssertJ toolset.
Dengan memperhitungkan jika anda don't ingin menggunakan catch-pengecualian alat, saya akan merekomendasikan tua yang baik dengan cara yang coba-coba
-menangkap
block, setidaknya hingga JDK7. Dan untuk JDK 8 pengguna anda mungkin lebih memilih untuk menggunakan AssertJ seperti ini mungkin lebih dari sekedar menegaskan pengecualian.
5. Dengan JDK8, lambdas masukkan tes tkp, dan mereka telah terbukti menjadi cara yang menarik untuk menegaskan dengan perilaku. AssertJ telah diperbarui untuk menyediakan pengalaman API untuk menegaskan dengan perilaku.
Dan sampel uji dengan AssertJ :
@Tes public void test_exception_approach_1() { ... assertThatExceptionOfType(IOException.class) .isThrownBy(() -> someBadIOOperation()) .withMessage("boom!"); }
@Tes public void test_exception_approach_2() { ... assertThatThrownBy(() -> someBadIOOperation()) .isInstanceOf(Exception.class) .hasMessageContaining("boom"); }
@Tes public void test_exception_approach_3() { ... // ketika Throwable dilemparkan = catchThrowable(() -> someBadIOOperation());
// kemudian
assertThat(dibuang).isInstanceOf(Exception.class)
.hasMessageContaining("boom");
}
6. Dengan dekat penulisan ulang lengkap dari JUnit 5, pernyataan telah peningkatan sedikit, mereka dapat membuktikan menarik sebagai out of the box cara untuk menyatakan benar pengecualian. Tapi benar-benar pernyataan API masih agak miskin, ada's tidak ada di luar assertThrows
.
@Tes
@DisplayName("melempar EmptyStackException ketika mengintip")
batal throwsExceptionWhenPeeked() {
Throwable t = assertThrows(EmptyStackException.class, () -> stack.peek());
Asersi.assertEquals("...", t.getMessage());
}
Seperti yang anda perhatikan assertEquals
masih kembali batal
, dan karena itu doesn't memungkinkan chaining pernyataan seperti AssertJ.
Juga jika anda ingat nama bentrokan dengan Matcher
atau Menegaskan
, bersiaplah untuk bertemu dengan bentrokan yang sama dengan Pernyataan
.
I'd seperti untuk menyimpulkan bahwa hari ini (2017-03-03) AssertJ's mudah digunakan, mudah ditemukan API, laju pembangunan dan sebagai de facto uji ketergantungan adalah solusi terbaik dengan JDK8 terlepas dari kerangka pengujian (JUnit atau tidak), sebelum JDKs malah harus bergantung pada coba-coba
-menangkap
blok bahkan jika mereka merasa kikuk.
This jawaban yang telah disalin dari pertanyaan lain yang don't yang sama-sama memiliki visibilitas, saya penulis yang sama.
Sekarang bahwa JUnit 5 telah dirilis, pilihan terbaik adalah dengan menggunakan Pernyataan.assertThrows()
(lihat
the Junit 5 User Guide).
Berikut adalah contoh yang memverifikasi pengecualian dilemparkan, dan menggunakan Kebenaran untuk membuat pernyataan pada pesan pengecualian:
public class FooTest {
@Test
public void doStuffThrowsIndexOutOfBoundsException() {
Foo foo = new Foo();
IndexOutOfBoundsException e = assertThrows(
IndexOutOfBoundsException.class, foo::doStuff);
assertThat(e).hasMessageThat().contains("woops!");
}
}
Keuntungan dari pendekatan dalam jawaban yang lain adalah:
melempar
klausulSebuah metode yang sama akan ditambahkan ke org.junit Menegaskan
di JUnit 4.13.
Bagaimana dengan ini: Menangkap sangat umum pengecualian, pastikan itu membuatnya keluar dari menangkap blok, kemudian menegaskan bahwa kelas pengecualian adalah apa yang anda berharap untuk menjadi. Ini menegaskan akan gagal jika a) pengecualian adalah jenis yang salah (misalnya. jika anda punya Null Pointer bukan) dan b) pengecualian itu't pernah dilemparkan.
public void testFooThrowsIndexOutOfBoundsException() {
Throwable e = null;
try {
foo.doStuff();
} catch (Throwable ex) {
e = ex;
}
assertTrue(e instanceof IndexOutOfBoundsException);
}
import static com.googlecode.catchexception.apis.BDDCatchException.*;
@Test
public void testFooThrowsIndexOutOfBoundsException() {
when(() -> foo.doStuff());
then(caughtException()).isInstanceOf(IndexOutOfBoundsException.class);
}
eu.codearte.catch-exception:catch-exception:2.0
Menggunakan AssertJ pernyataan, yang dapat digunakan bersama JUnit:
import static org.assertj.core.api.Assertions.*;
@Test
public void testFooThrowsIndexOutOfBoundsException() {
Foo foo = new Foo();
assertThatThrownBy(() -> foo.doStuff())
.isInstanceOf(IndexOutOfBoundsException.class);
}
It's lebih baik dari @Test(expected=IndexOutOfBoundsException.class)
karena itu adalah yang diharapkan baris di tes melemparkan pengecualian dan memungkinkan anda memeriksa rincian lebih lanjut tentang pengecualian, seperti pesan, lebih mudah:
assertThatThrownBy(() ->
{
throw new Exception("boom!");
})
.isInstanceOf(Exception.class)
.hasMessageContaining("boom");
Untuk memecahkan masalah yang sama saya lakukan set up sebuah proyek kecil: http://code.google.com/p/catch-exception/
Menggunakan pembantu kecil anda akan menulis
verifyException(foo, IndexOutOfBoundsException.class).doStuff();
Ini adalah kurang verbose dari ExpectedException aturan JUnit 4.7. Dibandingkan dengan solusi yang diberikan oleh skaffman, anda dapat menentukan di mana baris kode yang anda harapkan pengecualian. Saya harap ini membantu.
Update: JUnit5 memiliki peningkatan untuk pengecualian pengujian: assertThrows
.
berikut ini adalah contoh dari: Junit 5 User Guide
@Test
void exceptionTesting() {
Throwable exception = assertThrows(IllegalArgumentException.class, () ->
{
throw new IllegalArgumentException("a message");
});
assertEquals("a message", exception.getMessage());
}
Jawaban asli menggunakan JUnit 4.
Ada beberapa cara untuk menguji bahwa pengecualian dilemparkan. Saya juga telah dibahas di bawah ini pilihan saya posting Bagaimana untuk menulis besar unit tes dengan JUnit
Mengatur diharapkan
parameter @Test(diharapkan = FileNotFoundException.class)
.
@Test(expected = FileNotFoundException.class)
public void testReadFile() {
myClass.readFile("test.txt");
}
Menggunakan coba-coba
menangkap
public void testReadFile() {
try {
myClass.readFile("test.txt");
fail("Expected a FileNotFoundException to be thrown");
} catch (FileNotFoundException e) {
assertThat(e.getMessage(), is("The file test.txt does not exist!"));
}
}
Pengujian dengan ExpectedException
Aturan.
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void testReadFile() throws FileNotFoundException {
thrown.expect(FileNotFoundException.class);
thrown.expectMessage(startsWith("The file test.txt"));
myClass.readFile("test.txt");
}
Anda dapat membaca lebih lanjut tentang pengecualian pengujian di JUnit4 wiki untuk Pengecualian pengujian dan buruk.robot - Mengharapkan Pengecualian JUnit Aturan.
IMHO, cara terbaik untuk memeriksa pengecualian di JUnit adalah try/catch/gagal/menegaskan pola:
// this try block should be as small as possible,
// as you want to make sure you only catch exceptions from your code
try {
sut.doThing();
fail(); // fail if this does not throw any exception
} catch(MyException e) { // only catch the exception you expect,
// otherwise you may catch an exception for a dependency unexpectedly
// a strong assertion on the message,
// in case the exception comes from anywhere an unexpected line of code,
// especially important if your checking IllegalArgumentExceptions
assertEquals("the message I get", e.getMessage());
}
The assertTrue
mungkin sedikit kuat untuk beberapa orang, jadi assertThat(e.getMessage(), containsString("pesan");
mungkin lebih baik.
@Test
void testFooThrowsIndexOutOfBoundsException() {
Throwable exception = expectThrows( IndexOutOfBoundsException.class, foo::doStuff );
assertEquals( "some message", exception.getMessage() );
}
Lebih Infos tentang JUnit 5 di http://junit.org/junit5/docs/current/user-guide/#writing-tests-assertions
Saya mencoba banyak metode di sini, tapi mereka baik yang rumit atau tidak't cukup memenuhi kebutuhan saya. Pada kenyataannya, seseorang dapat menulis sebuah metode penolong cukup sederhana:
public class ExceptionAssertions {
public static void assertException(BlastContainer blastContainer ) {
boolean caughtException = false;
try {
blastContainer.test();
} catch( Exception e ) {
caughtException = true;
}
if( !caughtException ) {
throw new AssertionFailedError("exception expected to be thrown, but was not");
}
}
public static interface BlastContainer {
public void test() throws Exception;
}
}
Menggunakannya seperti ini:
assertException(new BlastContainer() {
@Override
public void test() throws Exception {
doSomethingThatShouldExceptHere();
}
});
Nol ketergantungan: tidak perlu untuk mockito, tidak perlu powermock; dan bekerja dengan baik dengan akhir kelas.
Yang paling fleksibel dan elegan jawaban untuk Junit 4 yang saya temukan di Mkyong blog. Ini memiliki fleksibilitas try/catch
menggunakan @Aturan
penjelasan. Saya menyukai pendekatan ini karena anda dapat membaca atribut tertentu yang disesuaikan terkecuali.
package com.mkyong;
import com.mkyong.examples.CustomerService;
import com.mkyong.examples.exception.NameNotFoundException;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.hasProperty;
public class Exception3Test {
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void testNameNotFoundException() throws NameNotFoundException {
//test specific type of exception
thrown.expect(NameNotFoundException.class);
//test message
thrown.expectMessage(is("Name is empty!"));
//test detail
thrown.expect(hasProperty("errCode")); //make sure getters n setters are defined.
thrown.expect(hasProperty("errCode", is(666)));
CustomerService cust = new CustomerService();
cust.findByName("");
}
}
Jika anda ingin solusi yang:
Berikut adalah fungsi utilitas yang saya tulis:
public final <T extends Throwable> T expectException( Class<T> exceptionClass, Runnable runnable )
{
try
{
runnable.run();
}
catch( Throwable throwable )
{
if( throwable instanceof AssertionError && throwable.getCause() != null )
throwable = throwable.getCause(); //allows "assert x != null : new IllegalArgumentException();"
assert exceptionClass.isInstance( throwable ) : throwable; //exception of the wrong kind was thrown.
assert throwable.getClass() == exceptionClass : throwable; //exception thrown was a subclass, but not the exact class, expected.
@SuppressWarnings( "unchecked" )
T result = (T)throwable;
return result;
}
assert false; //expected exception was not thrown.
return null; //to keep the compiler happy.
}
Menggunakannya sebagai berikut:
@Test
public void testThrows()
{
RuntimeException e = expectException( RuntimeException.class, () ->
{
throw new RuntimeException( "fail!" );
} );
assert e.getMessage().equals( "fail!" );
}
Dalam kasus saya, saya selalu mendapatkan RuntimeException dari db, tapi pesan yang berbeda. Dan pengecualian yang perlu ditangani masing-masing. Berikut adalah bagaimana saya diuji:
@Test
public void testThrowsExceptionWhenWrongSku() {
// Given
String articleSimpleSku = "999-999";
int amountOfTransactions = 1;
Exception exception = null;
// When
try {
createNInboundTransactionsForSku(amountOfTransactions, articleSimpleSku);
} catch (RuntimeException e) {
exception = e;
}
// Then
shouldValidateThrowsExceptionWithMessage(exception, MESSAGE_NON_EXISTENT_SKU);
}
private void shouldValidateThrowsExceptionWithMessage(final Exception e, final String message) {
assertNotNull(e);
assertTrue(e.getMessage().contains(message));
}