@BeforeClass ve miras - yürütme sırası


93

Birim testlerim için temel olarak kullandığım soyut bir temel sınıfım var (TestNG 5.10). Bu sınıfta, testlerim için tüm ortamı başlatırım, veritabanı eşlemeleri kurarım, vb. Bu soyut sınıf, @BeforeClassbaşlatmayı yapan bir açıklama içeren bir yönteme sahiptir .

Daha sonra, bu sınıfı @Testyöntemlere ve yöntemlere sahip olduğum belirli sınıflarla genişletiyorum @BeforeClass. Bu yöntemler, ortamın sınıfa özgü başlatılmasını sağlar (örneğin, veritabanına bazı kayıtlar koyar).

@BeforeClassAçıklamalı yöntemlerin belirli bir sırasını nasıl uygulayabilirim ? Soyut temel sınıftan olanların, genişleyen sınıfınkilerden önce çalıştırılmasına ihtiyacım var.

Misal:

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

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

    @Test
    doTests() {...}
}

Beklenen sipariş:

A.doInitialization
B.doSpecificInitialization
B.doTests

Gerçek sipariş:

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

Yanıtlar:


50

Koymayın @BeforeClassüzerine abstractsınıfa. Her alt sınıftan arayın.

abstract class A {
    void doInitialization() {}
}

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

    @Test
    void doTests() {}
}

TestNG'nin sahip olduğu gibi görünüyor @BeforeClass(dependsOnMethods={"doInitialization"})- bir deneyin.


10
Temelde kaçınmak istediğim şey buydu: süper (soyut) sınıfın yöntemlerini açıkça çağırmaya gerek yok. Özellikle A'dan miras alan ancak kendi @BeforeClass yöntemine sahip olmayan sınıflarım olduğu için. Sadece bu amaç için bir tane eklemem gerekiyor.
Dominik Sandjaja

5
dependsOnMethodsGeçici çözüm hile yaptı. "Önce süper sınıf" yaklaşımını tercih
etsem de

1
"SubsOnMethod" kullanmak için "doInitialization", "@Test" ile anotlanmamalı mı? Teknik olarak tek başına bir test olmadığı için bu bir problem ...
N3da

@BeforeClass statik bir yöntemi açıklamalı
Fabrizio Stellato

110

düzenleme: Aşağıdaki cevap JUnit içindir , ancak yine de burada bırakacağım, çünkü yardımcı olabilir.

JUnit api'ye göre : "Süper sınıfların @BeforeClass yöntemleri, mevcut sınıflardan önce çalıştırılacaktır."

Bunu test ettim ve benim için çalışıyor gibi görünüyor.

Ancak, aşağıda @Odys'in bahsettiği gibi, JUnit için farklı şekilde adlandırılmış iki yönteme sahip olmanız gerekir, ancak aksi takdirde yalnızca alt sınıf yönteminin çalıştırılmasına neden olur çünkü ebeveyn gölgelenir.


56
Asıl soru TestNG için olsa da, JUnit için googledikten sonra buraya geldim ve cevabınız yardımcı oldu - teşekkürler!
teabot

10
JUnit için size ihtiyacım olsa ebeveyn gölgeli olacak çünkü yürütülüyor sadece alt sınıf yöntemine neden olur aksi yapıyor olarak farklı adlandırılmış iki yöntem var.
Odys

3
@Odys, bundan bahsettiğiniz için çok teşekkür ederim. Alt sınıfımdaki "kurulum" yönteminin, üst sınıfındakinin çalışmadığı halde neden çalıştığını anlamaya çalışıyordum. Az önce beni bir ton kızgınlıktan kurtardın!
Tom Catullo

Günümü gün ettin. Teşekkürler!
raiks

7

publicSoyut sınıfa ekledim ve TestNG (6.0.1) daha önce doInitialization () işlemini gerçekleştirdi doTests. A sınıfından doInitialization()çıkarırsam TestNG çalışmıyor public.

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

class B extends A {    
 @Test
 doTests() {...}
}

1
Bu doğru ama alakasız. Sınıf, bu işe yaramazsa B da bir sahiptir @BeforeClassOP'ın durumunda olduğu gibi, -annotated yöntem.
jpaugh

1
Ben de aynısını yaptım. Temel yöntem özelse kalıtım sırası gözden kaçmış gibi görünüyor. Teşekkürler!
Manu

6

Örneğinizi 5.11 ile denedim ve ilk olarak temel sınıfın @BeforeClass öğesini alıyorum.

Testng.xml dosyanızı gönderebilir misiniz? Belki orada hem A hem de B'yi belirtiyorsunuz, ancak yalnızca B gerekli.

Testng-users posta listesini takip etmekten çekinmeyin ve sorununuza daha yakından bakabiliriz.

- Cedric


2
Tanımlanan testng için .xml yok (açıkça), Eclipse ve Maven'den çalıştırılıyor.
Dominik Sandjaja

Eclipse'den tam olarak nasıl çalıştırıyorsunuz? B sınıfına sağ tıklamak mı?
Cedric Beust

4

Bunun üzerinden geçtim ve bunu başarmanın bir yolunu daha buldum. Sadece kullanmak alwaysRunüzerine @BeforeClassveya @BeforeMethodbeklediğiniz gibi soyut sınıfta çalışır.

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

2

Şuradan çalıştırdığımda: JUnitCore.runClasses (TestClass.class); Üst öğeyi çocuktan önce düzgün bir şekilde çalıştıracaktır ( super.SetUpBeforeClass () 'a ihtiyacınız yoktur; ) Eclipse'den çalıştırırsanız: Bazı nedenlerden dolayı temel sınıfı çalıştıramaz. Çözüm: Temel sınıfı açıkça çağırın: ( BaseTest.setUpBeforeClass (); ) Zaten kurulup kurulmadığını belirlemek için bir uygulamadan çalıştırmanız durumunda temel sınıfta bir bayrak olmasını isteyebilirsiniz. Bu nedenle, her iki olası yöntemle çalıştırırsanız yalnızca bir kez çalışır (örneğin, kişisel test için tutulmadan ve bir derleme sürümü için ANT aracılığıyla).

Bu, Eclipse ile ilgili bir hata veya en azından beklenmedik sonuçlar gibi görünüyor ..


2

JUnit için : @fortega'nın da belirttiği gibi: JUnit api'ye göre: "Süper sınıfların @BeforeClass yöntemleri mevcut sınıflardan önce çalıştırılacaktır."

Ancak her iki yöntemi de aynı adla adlandırmamaya dikkat edin . Bu durumda ebeveyn yöntemi çocuk ebeveyn tarafından gizlenecektir. Kaynak .


1

@BeforeClass yönteminizin, aşağıdaki gibi alt sınıflar tarafından üzerine yazılan veya yazılmayan boş bir specificBeforeClass () yöntemini çağırmasına ne dersiniz:

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

  protected void specificBeforeClass() {}
}

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

  // Tests
}

3
BeforeClass statik olmalıdır, bu yüzden bunu junit ile yapamazsınız
madx

1

dependsOnMethod kullanılabilir.

örneğin Bahar durumunda ( AbstractTestNGSpringContextTests)

@BeforeClass(alwaysRun = true, dependsOnMethods = "springTestContextPrepareTestInstance")

1

İthalat ekstrenizi kontrol edin. Olmalı

import org.testng.annotations.BeforeClass;

değil

import org.junit.BeforeClass;


1

Bu benim için çalışıyor -

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

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

       //do class specific init

    }   

    @Test
    doTests() {...}
}

1
Bu kodun ne yaptığı ve orijinal soruyu yanıtlayan kısa bir açıklama ekleyin
Cray

0

Neden süper sınıfınızda, superclass'taki BeforeClass açıklamalı yönteminizden çağrılan soyut bir doSpecialInit () yöntemi oluşturmaya çalışmıyorsunuz?

Dolayısıyla, sınıfınızı devralan geliştiriciler bu yöntemi uygulamaya zorlanır.


Dürüst olmak gerekirse, bu soruyu sorduğumdan bu yana geçen 3 1/2 yıl içinde mantık bile değişmiş olabilir ... ;-) Yani evet, belki bu bir fikirdi, belki işe yaramadı - dürüst olmak gerekirse hatırlamak.
Dominik Sandjaja

0

Burada başka bir kolay çözüm var.

Benim özel durumum, süper sınıftaki "BeforeClass" çalıştırılmadan önce alt sınıfa "BeforeClass" dan sahte hizmetler enjekte etmem gerektiğidir.

Bunu yapmak için - @ClassRulealt sınıfta basitçe a kullanın .

Örneğin:

@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
    }
};

Umarım bu yardımcı olur. Bu, statik kurulumu "ters" sırada etkili bir şekilde gerçekleştirebilir.


0

Bugün benzer bir sorunla karşılaştım, tek fark Base sınıfının soyut olmamasıydı

İşte benim durumum

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

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

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

@BeforeClassA sınıfından bir yöntemin asla çalıştırılmadığı ortaya çıktı.

  • A.doInitialization () -> BU ASLA sessizce YÜRÜTÜLMÜŞTÜR
  • B.doSpecificInitialization ()
  • B. doTests ()

Gizlilik değiştiricileri ile oynamak ı TestNG bulundu çalıştırmaz bir @BeforeClassmiras sınıftan açıklamalı yöntemini bir yöntem sınıf inheritor görünür değilse

Yani bu işe yarayacak:

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() {...}
}

Sonuç olarak aşağıdakiler gerçekleşir:

  • A.doInitialization ()
  • B.doSpecificInitialization ()
  • B. doTests ()

-1

Benim durumumda (JUnit), temel sınıfta ve türetilmiş sınıfta setup () adlı aynı yöntemlere sahibim. Bu durumda, yalnızca türetilmiş sınıfın yöntemi çağrılır ve ben de temel sınıf yöntemini çağırırım.


-2

Kalıtımı kullanarak bunu başarmanın daha iyi ve temiz bir yolu aşağıdaki gibi olabilir:

abstract class A {

    @BeforeClass
    void doInitialization() {}
}

class B extends A {

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

    @Test
    void doTests() {}
}

1
Parent sınıfındaki yöntemin çalıştırılmasına ihtiyacınız varsa, önce Child sınıfındaki yöntemi Parent öğesinden farklı bir şekilde adlandırmanız gerekir (çünkü aynı imzaya sahiplerse, polimorfizm devreye girer). Bunun daha temiz bir yol olduğuna inanıyorum.
Anatolii Stepaniuk
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.