JUnit kebingungan: gunakan 'extends TestCase' atau '@Test'?

152

Saya telah menemukan penggunaan yang tepat (atau setidaknya dokumentasi) JUnit sangat membingungkan. Pertanyaan ini berfungsi sebagai referensi di masa depan dan sebagai pertanyaan nyata.

Jika saya mengerti dengan benar, ada dua pendekatan utama untuk membuat dan menjalankan tes JUnit:

Approach A (JUnit 3-style): buat kelas yang memperluas TestCase, dan mulai metode pengujian dengan kata tersebut test. Saat menjalankan kelas sebagai JUnit Test (dalam Eclipse), semua metode yang dimulai dengan kata testdijalankan secara otomatis.

import junit.framework.TestCase;

public class DummyTestA extends TestCase {

    public void testSum() {
        int a = 5;
        int b = 10;
        int result = a + b;
        assertEquals(15, result);
    }
}

Approach B (JUnit 4-style): membuat kelas 'normal' dan menambahkan @Testanotasi ke metode. Perhatikan bahwa Anda TIDAK harus memulai metode dengan kata test.

import org.junit.*;
import static org.junit.Assert.*;

public class DummyTestB {

    @Test
    public void Sum() {
        int a = 5;
        int b = 10;
        int result = a + b;
        assertEquals(15, result);
    }
}

Menggabungkan keduanya tampaknya bukan ide yang baik, lihat misalnya pertanyaan stackoverflow ini :

Sekarang, pertanyaan saya:

  1. Apa pendekatan yang disukai , atau kapan Anda akan menggunakan salah satu daripada yang lain?
  2. Pendekatan B memungkinkan untuk menguji pengecualian dengan memperluas anotasi @Test seperti pada @Test(expected = ArithmeticException.class). Tetapi bagaimana Anda menguji pengecualian saat menggunakan pendekatan A?
  3. Saat menggunakan pendekatan A, Anda dapat mengelompokkan sejumlah kelas tes dalam suite tes seperti ini:

    TestSuite suite = new TestSuite("All tests");
    suite.addTestSuite(DummyTestA.class);
    suite.addTestSuite(DummyTestAbis.class);

    Tetapi ini tidak dapat digunakan dengan pendekatan B (karena setiap testclass harus mensubclass TestCase). Apa cara yang tepat untuk mengelompokkan tes untuk pendekatan B?

Sunting: Saya telah menambahkan versi JUnit ke kedua pendekatan

Rabarberski
sumber
Saya telah melihat extends TestCasedan kemudian setiap tes juga dijelaskan dengan @Testhanya untuk membingungkan hal-hal. :)
EM-Creations

Jawaban:

119

Perbedaannya agak mudah:

  • extending TestCaseadalah cara unit test ditulis dalam JUnit 3 (tentu saja masih didukung dalam JUnit 4)
  • menggunakan @Testanotasi adalah cara yang diperkenalkan oleh JUnit 4

Umumnya Anda harus memilih jalur anotasi, kecuali diperlukan kompatibilitas dengan JUnit 3 (dan / atau versi Java lebih awal dari Java 5). Cara baru memiliki beberapa keunggulan:

Untuk menguji pengecualian yang diharapkan dalam JUnit 3 TestCaseAnda harus membuat teks eksplisit.

public void testMyException() {
  try {
    objectUnderTest.myMethod(EVIL_ARGUMENT);
    fail("myMethod did not throw an Exception!");
  } catch (MyException e) {
    // ok!
    // check for properties of exception here, if desired
  }
}

JUnit 5 memperkenalkan perubahan API lain, tetapi masih menggunakan anotasi. @TestAnotasi baru adalah org.junit.jupiter.api.Test(yang lama "JUnit 4 satu org.junit.Test), tetapi kerjanya hampir sama dengan JUnit 4 satu.

Joachim Sauer
sumber
Bermanfaat dan jawaban menyeluruh, tapi saya tidak sepenuhnya mengerti "periksa pesan pengecualian". Memeriksa string yang dikodekan akan menjadi mimpi buruk pemeliharaan. Anda pasti bermaksud "memeriksa properti jenis pengecualian spesifik Anda".
thSoft
3
@thoft: ini tidak sering digunakan, tetapi kadang-kadang saya ingin memastikan bahwa metode pengecualian menyebutkan bidang yang menyinggung, misalnya. Maka yang sederhana assertTrue(e.getMessage().contains("foo"))bisa bermanfaat.
Joachim Sauer
1
Bahkan di JUnit4 ini adalah ungkapan penting ketika Anda harus memeriksa pesan atau properti pengecualian lainnya (seperti penyebabnya). The expectedMetode hanya memeriksa untuk jenis.
Yishai
@Yishai: itu benar, tetapi sebagian besar waktu saya sudah puas jika metode ini melempar jenis Pengecualian yang benar pada input yang bermasalah.
Joachim Sauer
Untuk alasan itu, JUnit 5 melakukan perubahan besar dalam pengujian pengecualian. assertThrows () is fantastic :-)
Marcus K.
25

Saya memiliki preferensi untuk JUnit 4 (pendekatan Annotation) karena saya merasa lebih fleksibel.

Jika Anda ingin membangun test suite di JUnit 4, Anda harus membuat kelas yang mengelompokkan semua tes seperti ini:

import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;


@RunWith(Suite.class)
@SuiteClasses({
    Test1.class,
    Test2.class,
    Test3.class,
    Test4.class
})public class TestSuite
{
 /* empty class */
}
Grimmy
sumber
15

Ada bagian yang tidak terjawab untuk pertanyaan Anda, dan itu adalah "Apa cara yang tepat untuk mengelompokkan tes untuk pendekatan B?"

Jawaban resmi adalah bahwa Anda membubuhi keterangan kelas dengan @RunWith (Suite.class) dan kemudian menggunakan anotasi @ Suite.SuiteClasses untuk mendaftar kelas. Ini adalah bagaimana pengembang JUnit melakukannya (mendaftar setiap kelas dalam sebuah suite secara manual). Dalam banyak hal pendekatan ini merupakan peningkatan, karena itu sepele dan intuitif untuk ditambahkan sebelum perilaku suite dan setelah suite (cukup tambahkan metode @BeforeClass dan @AfterClass ke kelas yang dijelaskan dengan @RunWith - jauh lebih baik daripada TestFixture lama ).

Namun, ia memiliki langkah mundur, karena anotasi tidak memungkinkan Anda untuk membuat daftar kelas secara dinamis, dan mengatasi masalah itu menjadi agak jelek. Anda harus subclass kelas Suite dan secara dinamis membuat array kelas di subclass dan meneruskannya ke konstruktor Suite, tetapi ini adalah solusi yang tidak lengkap karena subclass Suite lainnya (seperti Kategori) tidak bekerja dengannya dan pada dasarnya tidak mendukung koleksi kelas Tes dinamis.

Yishai
sumber
1
+1 untuk ini. Setelah memulai tugas untuk menulis solusi dinamis untuk menambahkan Tes ke TestSuite, saya harus memperluas TestCase di setiap Tes saya. Ini pada gilirannya telah melanggar Tes unit kerja sebelumnya yang menggunakan anotasi JUnit4 untuk menentukan pengecualian yang diharapkan. Pencarian saya untuk cara mengisi Test Suite secara dinamis telah membawa saya ke utas ini, dan khususnya jawaban Anda, yang saya yakin adalah salah satu dari beberapa alasan yang diinginkan untuk melanjutkan JUnit 3.
8bitjunkie
4

Anda harus menggunakan JUnit 4. Ini lebih baik.

Banyak kerangka kerja sudah mulai mencabut dukungan JUnit 3.8.

Ini dari dokumentasi referensi Spring 3.0:

[Peringatan] Hirarki kelas 3.8 JUnit lama sudah tidak digunakan lagi

Secara umum, Anda harus selalu mencoba menggunakan rilis stabil terbaru kerangka kerja saat Anda memulai sesuatu yang baru.

Espen
sumber
1
  1. Pendekatan "yang disukai" adalah menggunakan anotasi yang telah diperkenalkan sejak Junit 4. Mereka membuat banyak hal lebih mudah (lihat pertanyaan kedua Anda)

  2. Anda dapat menggunakan blok coba / tangkap sederhana untuk itu:


public void testForException() {
    try {
        Integer.parseInt("just a string");
        fail("Exception should have been thrown");
    } catch (final Exception e) {
        // expected
    }
}
black666
sumber