Junit - jalankan metode pengaturan sekali

119

Saya menyiapkan kelas dengan beberapa pengujian dan daripada menggunakan, @Beforesaya ingin memiliki metode penyiapan yang hanya dijalankan sekali sebelum semua pengujian. Apakah itu mungkin dengan Junit 4.8?

Bober02
sumber
1
Lihat di RunListener: stackoverflow.com/a/14773170/548473
Grigory Kislin

Jawaban:

205

Meskipun saya setuju dengan @assylias bahwa menggunakan @BeforeClassadalah solusi klasik tidak selalu nyaman. Metode yang dianotasi @BeforeClassharus statis. Sangat merepotkan untuk beberapa tes yang membutuhkan contoh kasus uji. Misalnya pengujian berbasis Spring yang digunakan @Autowireduntuk bekerja dengan layanan yang ditentukan dalam konteks pegas.

Dalam hal ini saya pribadi menggunakan setUp()metode biasa yang dianotasi dengan @Beforeanotasi dan mengelola bendera custom static(!) Saya boolean:

private static boolean setUpIsDone = false;
.....
@Before
public void setUp() {
    if (setUpIsDone) {
        return;
    }
    // do the setup
    setUpIsDone = true;
}
AlexR
sumber
10
Menambah komentar Kenny Cason mengapa itu harus statis. Ini harus statis karena JUnit membuat instance baru dari kelas pengujian untuk setiap metode @Test. Variabel instance akan disetel ulang ke nilai defaultnya (salah) untuk setiap instance jika tidak statis. Lihat info lebih lanjut: martinfowler.com/bliki/JunitNewInstance.html
dustin.schultz
2
Ini berfungsi kecuali dalam kasus di mana setUp()metode dalam superclass - telah memposting jawaban di bawah ini mencoba untuk menyelesaikan ini.
Steve Chambers
4
Saya ragu untuk mengatakan ini kepada seseorang dengan perwakilan 84k, tetapi BeforeClass sebenarnya tidak menjawab pertanyaan: BeforeClass dijalankan di awal setiap kelas pengujian. Tapi OP meminta satu yang berjalan "hanya sekali sebelum semua tes". Solusi yang Anda usulkan dapat melakukan ini, tetapi Anda harus membuat semua kelas pengujian Anda memperpanjang kelas "CommonTest" ...
mike rodent
1
@mikerodent, IMHO OP menanyakan tentang semua tes dalam test case-nya, tidak semua tes secara keseluruhan. Jadi, komentar Anda kurang relevan. BTW, jangan khawatir untuk mengatakan apa pun kepada siapa pun meskipun reputasinya tinggi. Setidaknya inilah yang saya lakukan :). Dan reputasi saya jauh lebih rendah pada Agustus 2012 ketika saya menjawab pertanyaan itu.
AlexR
Tidak berfungsi untuk kasus saya, variabel yang diinisialisasi dalam pengaturan disetel ulang setelah setiap pengujian, jadi tidak ada gunanya melakukan init hanya sekali.
Aphax
89

Anda dapat menggunakan ini BeforeClasspenjelasan :

@BeforeClass
public static void setUpClass() {
    //executed only once, before the first test
}
assylias
sumber
12
Saya tidak dapat menggunakan ini, saya memiliki beberapa metode penyiapan yang didasarkan pada komponen non-statis seperti getClass ()
Bober02
1
@ Bober02 BeforeClass memang harus statis. Jika Anda tidak dapat menggunakannya, jawaban lain memberikan solusi.
assylias
2
Yakin Anda tidak dapat menggunakan TheClassYouWant.classselain panggilan getClass () Anda? Ini adalah Java yang sebenarnya: String.class.getName().
stolsvik
1
@mikerodent Saya memahami pertanyaan sebagai "semua tes di kelas" - tetapi Anda benar itu mungkin bukan yang diinginkan OP.
assylias
29

JUnit 5 sekarang memiliki anotasi @BeforeAll:

Menunjukkan bahwa metode yang dianotasi harus dijalankan sebelum semua metode @Test di kelas atau hierarki kelas saat ini; analog dengan @BeforeClass milik JUnit 4. Metode seperti itu harus statis.

Anotasi siklus hidup JUnit 5 tampaknya akhirnya benar! Anda dapat menebak anotasi mana yang tersedia tanpa perlu melihat (mis. @BeforeEach @AfterAll)

Brian
sumber
6
Ini memiliki masalah yang sama @BeforeClass, itu perlu static. Solusi IMO @ AlexR lebih bagus.
zengr
@ zengr cenderung setuju dengan Anda: seperti yang telah saya katakan kepada AlexR, solusinya mengharuskan semua kelas pengujian untuk subkelas dari kelas CommonTest jika hanya untuk dijalankan sekali. Tapi sesederhana mungkin, dan IMHO Anda mungkin tidak boleh menggunakan solusi kerangka kerja "mewah" yang disediakan ketika mekanisme sederhana tersedia dari bahasa. Kecuali jika ada alasan bagus tentunya. Juga, menggunakan hal sederhana seperti miliknya, dengan nama jenis "melakukan apa yang tertulis di kaleng" yang bagus, membantu keterbacaan.
mike rodent
Karena itu, sekali lagi IMHO, tampaknya ada lebih banyak pembenaran untuk memiliki anotasi "Lagi pula": akan sangat sulit dan dibuat-buat untuk merancang mekanisme untuk mendeteksi ketika semua tes dilakukan. Sebaliknya, tentu saja, para puritan mungkin akan berkata bahwa Anda tidak perlu melakukan "pembersihan akhir", yaitu setiap "tearDown" harus membiarkan semua sumber daya dalam keadaan bersih ... dan mereka mungkin benar!
mike rodent
Apakah ini berfungsi dengan Maven di mana ada beberapa modul, masing-masing dengan pengujiannya?
Mark Boon
@mike rodent, dalam kasus saya menyiapkan dan menghancurkan file uji di sistem file sebelum / setelah setiap pengujian tampaknya menyebabkan kebuntuan pada file. Untuk saat ini, saya telah sampai pada solusi AlexR untuk mengatur sekali. Saya memiliki dua flag statis, sudahSetup dan dirty. setup () memanggil cleanup () jika kondisi kotor terdeteksi pada awalnya, atau jika kegagalan pengaturan mengarah ke status kotor. Untuk membersihkan setelah menjalankan tes, saya menjalankannya lagi. Berantakan, tidak ideal sama sekali, tidak dalam proses pembuatan kami. Masih mencari cara yang lebih baik (jUnit 4.12).
Rebeccah
9

Saat setUp()berada di superclass dari kelas tes (misalnya di AbstractTestBasebawah), jawaban yang diterima dapat dimodifikasi sebagai berikut:

public abstract class AbstractTestBase {
    private static Class<? extends AbstractTestBase> testClass;
    .....
    public void setUp() {
        if (this.getClass().equals(testClass)) {
            return;
        }

        // do the setup - once per concrete test class
        .....
        testClass = this.getClass();
    }
}

Ini seharusnya bekerja untuk satu setUp()metode non-statis tetapi saya tidak dapat menghasilkan yang setara tearDown()tanpa tersesat ke dunia refleksi yang kompleks ... Bounty menunjuk kepada siapa saja yang bisa!

Steve Chambers
sumber
3

Sunting: Saya baru tahu saat men-debug bahwa kelas tersebut dibuat sebelum setiap tes juga. Saya kira anotasi @BeforeClass adalah yang terbaik di sini.

Anda juga dapat menyiapkan konstruktor, bagaimanapun juga, kelas pengujian adalah kelas. Saya tidak yakin apakah ini praktik yang buruk karena hampir semua metode lain diberi anotasi, tetapi berhasil. Anda bisa membuat konstruktor seperti itu:

public UT () {
    // initialize once here
}
@Test
// Some test here...

Ctor akan dipanggil sebelum pengujian karena tidak statis.


sumber
0

Coba solusi ini: https://stackoverflow.com/a/46274919/907576 :

dengan @BeforeAllMethods/ @AfterAllMethodsannotation Anda dapat menjalankan metode apa pun di kelas Test dalam konteks instance, di mana semua nilai yang dimasukkan tersedia.

radistao
sumber
Mengandalkan perpustakaan pihak ketiga.
Andrew
0

Solusi kotor saya adalah:

public class TestCaseExtended extends TestCase {

    private boolean isInitialized = false;
    private int serId;

    @Override
    public void setUp() throws Exception {
        super.setUp();
        if(!isInitialized) {
            loadSaveNewSerId();
            emptyTestResultsDirectory();
            isInitialized = true;
        }
    }

   ...

}

Saya menggunakannya sebagai basis dasar untuk semua testCases saya.

Obi Dua
sumber
public class TestCaseExtended memperluas TestCase {private static boolean isInitialized = false; pribadi TestCaseExtended caseExtended statis; private int serId; @Override public void setUp () menampilkan Exception {super.setUp (); if (! isInitialized) {caseExtended = new TestCaseExtended (); caseExtended.loadSaveNewSerId (); caseExtended.emptyTestResultsDirectory (); isInitialized = true; }}
Obi Two
0

Jika Anda tidak ingin memaksakan deklarasi variabel yang disetel dan diperiksa pada setiap subtes, maka menambahkan ini ke SuperTest bisa dilakukan:

public abstract class SuperTest {

    private static final ConcurrentHashMap<Class, Boolean> INITIALIZED = new ConcurrentHashMap<>();
    protected final boolean initialized() {
        final boolean[] absent = {false};
        INITIALIZED.computeIfAbsent(this.getClass(), (klass)-> {
            return absent[0] = true;
        });
        return !absent[0];
    }
}



public class SubTest extends SuperTest {
    @Before
    public void before() {
        if ( super.initialized() ) return;

         ... magic ... 
    }

}
mmm
sumber
0

Saya memecahkan masalah ini seperti ini:

Tambahkan ke kelas abstrak Basis Anda (maksud saya kelas abstrak tempat Anda menginisialisasi driver dalam metode setUpDriver () ) bagian kode ini:

private static boolean started = false;
static{
    if (!started) {
        started = true;
        try {
            setUpDriver();  //method where you initialize your driver
        } catch (MalformedURLException e) {
        }
    }
}

Dan sekarang, jika kelas pengujian Anda akan diperluas dari kelas abstrak Dasar -> metode setUpDriver () akan dijalankan sebelum @Test pertama hanya SATU kali per proses.

Sergii
sumber
0

Gunakan metode @PostConstruct Spring untuk melakukan semua pekerjaan inisialisasi dan metode ini berjalan sebelum @Test mana pun dijalankan

Abhishek Chatterjee
sumber