@BeforeClass dan inheritance - urutan eksekusi

92

Saya memiliki kelas dasar abstrak, yang saya gunakan sebagai dasar untuk pengujian unit saya (TestNG 5.10). Di kelas ini, saya menginisialisasi seluruh lingkungan untuk pengujian saya, menyiapkan pemetaan database, dll. Kelas abstrak ini memiliki metode dengan @BeforeClassanotasi yang melakukan inisialisasi.

Selanjutnya, saya memperluas kelas itu dengan kelas tertentu di mana saya memiliki @Testmetode dan juga @BeforeClassmetode. Metode ini melakukan inisialisasi lingkungan khusus kelas (misalnya, memasukkan beberapa catatan ke dalam database).

Bagaimana saya dapat menerapkan urutan tertentu dari @BeforeClassmetode beranotasi? Saya membutuhkan yang dari kelas dasar abstrak untuk dieksekusi sebelum kelas yang diperluas.

Contoh:

abstract class A {
    @BeforeClass
    doInitialization() {...}
}

class B extends A {
    @BeforeClass
    doSpecificInitialization() {...}

    @Test
    doTests() {...}
}

Pesanan yang diharapkan:

A.doInitialization
B.doSpecificInitialization
B.doTests

Urutan sebenarnya:

B.doSpecificInitialization // <- crashes, as the base init is missing
(A.doInitialization        // <---not executed
 B.doTests)                // <-/
Dominik Sandjaja
sumber

Jawaban:

50

Jangan taruh @BeforeClassdi abstractkelas. Sebut saja dari setiap subclass.

abstract class A {
    void doInitialization() {}
}

class B extends A {
    @BeforeClass
    void doSpecificInitialization() {
        super.doInitialization();
    }

    @Test
    void doTests() {}
}

Sepertinya TestNG memiliki @BeforeClass(dependsOnMethods={"doInitialization"})- cobalah.

Bozho
sumber
10
Pada dasarnya itulah yang ingin saya hindari: tidak perlu secara eksplisit memanggil metode kelas super (abstrak). Terutama karena saya juga memiliki kelas, yang mewarisi dari A tetapi tidak memiliki metode @BeforeClass sendiri. Saya harus memasukkan satu hanya untuk tujuan itu.
Dominik Sandjaja
5
The dependsOnMethodssolusi melakukan trik. Meskipun saya lebih suka pendekatan "superclass first" ...
Dominik Sandjaja
1
Untuk menggunakan "dependOnMethod", tidakkah seharusnya "doInitialization" dianotasi dengan "@Test"? Itu adalah masalah karena secara teknis ini bukan tes dengan sendirinya ...
N3da
@Beforelass harus memberi anotasi metode statis
Fabriio Stellato
109

edit: Jawaban di bawah ini adalah untuk JUnit , tetapi saya akan meninggalkannya di sini, karena ini bisa membantu.

Menurut JUnit api : "Metode superclass @BeforeClass akan dijalankan sebelum kelas saat ini."

Saya menguji ini, dan tampaknya berhasil untuk saya.

Namun, seperti yang disebutkan @Odys di bawah ini, untuk JUnit Anda harus memiliki dua metode yang diberi nama berbeda meskipun melakukan sebaliknya hanya akan menghasilkan metode subclass yang dijalankan karena induknya akan dibayangi.

Fortega
sumber
56
Meskipun pertanyaan asli untuk TestNG, saya tiba di sini setelah googling untuk JUnit dan jawaban Anda membantu - terima kasih!
teko teh
9
untuk JUnit Anda harus memiliki dua metode yang diberi nama berbeda meskipun melakukan sebaliknya hanya akan menghasilkan metode subclass yang dijalankan karena induknya akan dibayangi.
Odys
2
@Odys, terima kasih banyak telah menyebutkan ini. Saya berjuang untuk mencari tahu mengapa metode "pengaturan" di subkelas saya berjalan sementara yang ada di kelas supernya tidak. Anda baru saja menyelamatkan saya dari satu ton kejengkelan!
Tom Catullo
Anda membuat hari saya. Terima kasih!
raik
7

Saya menambahkan publicke kelas abstrak dan TestNG (6.0.1) mengeksekusi doInitialization () sebelumnya doTests. TestNG tidak dijalankan doInitialization()jika saya menghapus publicdari kelas A.

public abstract class A {
 @BeforeClass
 doInitialization() {...}
}

class B extends A {    
 @Test
 doTests() {...}
}
Paul
sumber
1
Itu benar, tapi tidak relevan. Ini tidak bekerja ketika kelas B juga memiliki @BeforeClassmetode -annotated, seperti dalam kasus OP.
jpaugh
1
Saya melakukan hal yang sama. Tampaknya urutan pewarisan terlewat jika metode dasarnya bersifat pribadi. Terima kasih!
Manu
6

Saya baru saja mencoba contoh Anda dengan 5.11 dan saya mendapatkan @BeforeClass dari kelas dasar yang dipanggil terlebih dahulu.

Bisakah Anda memposting file testng.xml Anda? Mungkin Anda menentukan A dan B di sana, sementara hanya B yang diperlukan.

Jangan ragu untuk menindaklanjuti milis pengguna testng dan kami dapat melihat lebih dekat masalah Anda.

- Cedric

Cedric Beust
sumber
2
Tidak .xml untuk testng didefinisikan (secara eksplisit), itu dijalankan dari Eclipse dan Maven.
Dominik Sandjaja
Bagaimana Anda menjalankannya dari Eclipse? Mengklik kanan di kelas B?
Cedric Beust
4

Saya baru saja melalui ini dan menemukan satu cara lagi untuk mencapai ini. Cukup gunakan alwaysRundi @BeforeClassatau @BeforeMethoddi kelas abstrak, berfungsi seperti yang Anda harapkan.

public class AbstractTestClass {
    @BeforeClass(alwaysRun = true)
    public void generalBeforeClass() {
        // do stuff
        specificBeforeClass();
    }
}
Konrad Garus
sumber
2

Ketika saya menjalankan dari: JUnitCore.runClasses (TestClass.class); Ini akan mengeksekusi orang tua dengan benar, sebelum anak (Anda tidak perlu super.SetUpBeforeClass (); ) Jika Anda menjalankannya dari Eclipse: Untuk beberapa alasan gagal menjalankan kelas dasar. Solusi: Panggil kelas dasar secara eksplisit: ( BaseTest.setUpBeforeClass (); ) Anda mungkin ingin memiliki bendera di kelas dasar jika Anda menjalankannya dari aplikasi, untuk menentukan apakah sudah disiapkan atau belum. Jadi itu hanya berjalan sekali jika Anda menjalankannya melalui kedua metode yang memungkinkan (seperti dari gerhana untuk pengujian pribadi, dan melalui ANT untuk rilis build).

Tampaknya ini bug dengan Eclipse, atau setidaknya hasil yang tidak diharapkan ..

Steve
sumber
2

Untuk JUnit : Seperti yang telah disebutkan @fortega: Menurut JUnit api: "Metode superclass @BeforeClass akan dijalankan sebelum kelas saat ini."

Namun hati-hati jangan sampai memberi nama kedua metode tersebut dengan nama yang sama . Karena dalam kasus ini metode induk akan disembunyikan oleh anak induk. Sumber .

Anatolii Stepaniuk
sumber
1

Bagaimana jika metode @BeforeClass Anda memanggil metode specificBeforeClass () kosong yang mungkin atau mungkin tidak ditimpa oleh sub kelas seperti ini:

public class AbstractTestClass {
  @BeforeClass
  public void generalBeforeClass() {
    // do stuff
    specificBeforeClass();
  }

  protected void specificBeforeClass() {}
}

public class SpecificTest {
  @Override
  protected void specificBeforeClass() {
    // Do specific stuff
  }

  // Tests
}
Tatome
sumber
3
BeforeClass harus statis sehingga Anda tidak dapat melakukan ini dengan junit
madx
1

dependsOnMethod dapat digunakan.

misalnya dalam kasus Musim Semi ( AbstractTestNGSpringContextTests)

@BeforeClass(alwaysRun = true, dependsOnMethods = "springTestContextPrepareTestInstance")
Sandeep Jindal
sumber
1

Periksa pernyataan impor Anda. Harus

import org.testng.annotations.BeforeClass;

tidak

import org.junit.BeforeClass;

nishantrevo.dll
sumber
1

Ini bekerja untuk saya -

abstract class A {
    @BeforeClass
    doInitialization() {...}
}

class B extends A {
    @Override
    @BeforeClass
    doInitialization() { 

       //do class specific init

    }   

    @Test
    doTests() {...}
}
srikar
sumber
1
Tambahkan penjelasan singkat tentang fungsi kode ini dan menjawab pertanyaan asli
Cray
0

Mengapa Anda tidak mencoba membuat metode abstrak doSpecialInit () di kelas super Anda, yang dipanggil dari metode beranotasi BeforeClass di superclass.

Jadi, pengembang yang mewarisi kelas Anda dipaksa untuk mengimplementasikan metode ini.

Ludo
sumber
Sejujurnya, bahkan logikanya mungkin telah berubah dalam 3 1/2 tahun terakhir sejak saya mengajukan pertanyaan ini ... ;-) Jadi ya, mungkin ini adalah ide, mungkin tidak berhasil - sejujurnya saya tidak ingat.
Dominik Sandjaja
0

Ada solusi mudah lainnya di sini.

Situasi khusus saya adalah bahwa saya perlu menyuntikkan layanan tiruan dari "BeforeClass" di subclass sebelum "BeforeClass" di superclass tersebut dijalankan.

Untuk melakukan ini - cukup gunakan a @ClassRuledi subclass.

Sebagai contoh:

@ClassRule
public static ExternalResource mocksInjector = new ExternalResource() {
    @Override
    protected void before() {
        // inject my mock services here
        // Note: this is executed before the parent class @BeforeClass
    }
};

Saya harap ini membantu. Ini secara efektif dapat menjalankan pengaturan statis dalam urutan "terbalik".

vikingsteve.dll
sumber
0

Saya telah menghadapi masalah serupa hari ini, satu-satunya perbedaan adalah kelas Base tidak abstrak

Ini kasus saya

public class A {
    @BeforeClass
    private void doInitialization() {...}
}

public class B extends A {
    @BeforeClass
    private void doSpecificInitialization() {...}

    @Test
    public void doTests() {...}
}

Ternyata @BeforeClassmetode dari kelas A tidak pernah dijalankan.

  • A.doInitialization () -> INI TIDAK PERNAH DILAKSANAKAN secara diam-diam
  • B. doSpecificInitialization ()
  • B. doTests ()

Bermain dengan pengubah privasi saya menemukan bahwa TestNG tidak akan mengeksekusi sebuah @BeforeClassmetode dijelaskan dari kelas mewarisi jika metode ini tidak terlihat dari kelas-pewaris

Jadi ini akan berhasil:

public class A {
    @BeforeClass
    private void doInitialization() {...}
}

public class B extends A {
    @BeforeClass
    //Here a privacy modifier matters -> please make sure your method is public or protected so it will be visible for ancestors
    protected void doSpecificInitialization() {...}

    @Test
    public void doTests() {...}
}

Akibatnya hal berikut terjadi:

  • A. doInitialization ()
  • B. doSpecificInitialization ()
  • B. doTests ()
rodikno
sumber
-1

Dalam kasus saya (JUnit), saya memiliki metode yang sama yang disebut setup () di kelas dasar dan kelas turunan. Dalam hal ini hanya metode kelas turunan yang dipanggil, dan saya menyebutnya metode kelas dasar.

Mike Sokolov
sumber
-2

Cara yang lebih baik dan lebih bersih untuk mencapai ini dengan menggunakan warisan mungkin sebagai berikut -

abstract class A {

    @BeforeClass
    void doInitialization() {}
}

class B extends A {

    @Override
    @BeforeClass
    void doInitialization() {
        super.doInitialization();
    }

    @Test
    void doTests() {}
}
kushal
sumber
1
Jika Anda memerlukan metode di kelas Parent untuk dieksekusi terlebih dahulu, Anda hanya perlu memberi nama metode di kelas Child berbeda dari Parent (karena jika mereka memiliki tanda tangan yang sama maka polimorfisme akan beraksi). Saya yakin ini adalah cara yang lebih bersih.
Anatolii Stepaniuk