Junit @ Before / @ After çağrılarının sırası nedir?


134

Entegrasyon Test Paketim var. IntegrationTestBaseTüm testlerimin uzatılması için bir dersim var . Bu temel sınıf, API ve DB bağlantılarını kurmak için bir @Before( public void setUp()) ve @After( public void tearDown()) yöntemine sahiptir. Yaptığım şey, her test senaryosunda bu iki yöntemi geçersiz kılmak ve super.setUp()ve super.tearDown(). Ancak bu, birisi amiri aramayı unutursa veya onu yanlış yere koyarsa ve bir istisna atılırsa ve sonunda süper çağırmayı unutursa sorunlara neden olabilir.

Ne yapmak istediğinizi yapmak olduğunu setUpve tearDowntemel sınıf yöntemleri finalve daha sonra sadece kendi açıklamalı ekleyin @Beforeve @Afteryöntemler. Bazı ilk testler yapmak, her zaman şu sırayla çağırıyor gibi görünüyor:

Base @Before
Test @Before
Test
Test @After
Base @After

ancak siparişin garanti edilmediği ve sorunlara neden olabileceği konusunda biraz endişeliyim. Etrafıma baktım ve konu hakkında hiçbir şey görmedim. Bunu yapıp yapamayacağımı ve sorun yaşamadığımı bilen var mı?

Kod:

public class IntegrationTestBase {

    @Before
    public final void setUp() { *always called 1st?* }

    @After
    public final void tearDown() { *always called last?* }
}


public class MyTest extends IntegrationTestBase {

    @Before
    public final void before() { *always called 2nd?* }

    @Test
    public void test() { *always called 3rd?* }

    @After
    public final void after() { *always called 4th?* }
}

1
MyTestbir eksik extends?
aioobe

@aioobe: artık değil :)
Joel

Yanıtlar:


136

Evet, bu davranış garantilidir:

@Before:

Üst @Beforesınıfların yöntemleri, mevcut sınıfta geçersiz kılınmadıkları sürece, mevcut sınıfın yöntemlerinden önce çalıştırılacaktır. Başka bir sıralama tanımlanmamıştır.

@After:

Üst @Aftersınıflarda bildirilen yöntemler, geçerli sınıfta geçersiz kılınmadıkları sürece, geçerli sınıfın yöntemlerinden sonra çalıştırılacaktır.


15
Açık olmak gerekirse, tüm @Beforeyöntemlerin uygulama sırası garanti edilmez. 10 @Beforeyöntem varsa, her biri herhangi bir sırayla yürütülebilir; diğer yöntemlerden hemen önce.
Swati

5
Öyleyse, belirsiz bir dokümantasyondan alıntı yapmak yerine, lütfen kendi kelimelerinizle açıklayabilir misiniz? Are @Beforeve @Afteryöntemler daha önce çalıştırmak her (Sınıf başına bir kez) hemen sınıf yöntemlerinin tam paketine önce ve sonra diğer sınıf yöntemi (yöntemin başına bir kez) ya da?
BT

5
John Q Citizen tarafından belirtilen önemli kısma bakın: "Bu, yalnızca @Before ile işaretlenen her yöntemin sınıf hiyerarşisinde benzersiz bir adı varsa geçerlidir" Hatırlanması çok önemli!
Bruno Bossola

Bir sınıfta @Before (d) yönteminde aynı yöntem adını ve süper sınıfındaki başka bir yöntemi kullanarak bir ad çakışması yaşadım junit-4.12.
Stephane

Bu kural, ConcordionRunner'ın @BeforeExample yöntemleri için de geçerli mi?
Adrian Pronk

51

Beni daha önce ısıran potansiyel bir şey:

@BeforeHer test sınıfında en fazla bir yönteme sahip olmayı seviyorum , çünkü @Beforebir sınıf içinde tanımlanan yöntemlerin çalıştırılma sırası garanti edilmiyor. Tipik olarak böyle bir yöntem arayacağım setUpTest().

Ancak, @Beforeolarak belgelenmesine rağmen The @Before methods of superclasses will be run before those of the current class. No other ordering is defined., bu yalnızca ile işaretlenen her yöntemin @Beforesınıf hiyerarşisinde benzersiz bir ada sahip olması durumunda geçerlidir .

Örneğin, şunlara sahiptim:

public class AbstractFooTest {
  @Before
  public void setUpTest() { 
     ... 
  }
}

public void FooTest extends AbstractFooTest {
  @Before
  public void setUpTest() { 
    ...
  }
}

Daha AbstractFooTest.setUpTest()önce kaçmayı bekliyordum FooTest.setUpTest()ama sadece FooTest.setupTest()idam edildim . AbstractFooTest.setUpTest()hiç çağrılmadı.

Çalışması için kod aşağıdaki şekilde değiştirilmelidir:

public void FooTest extends AbstractFooTest {
  @Before
  public void setUpTest() {
    super.setUpTest();
    ...
  }
}

Neden temel sınıftaki @Before yönteminin adını değiştirmiyorsunuz? Bu sizi tüm çocuklarda amiri aramak zorunda kalmaktan kurtaracak ... her neyse, aynı isim sorunuyla iyi bir yakalayış
Lawrence Tierney

24
İşleri daha güvenli hale getirmek için sadece bir açıklama: İsim çatışmalarını önlemek için temel sınıfta @Before/ @Afteryöntem (ler) i oluşturabilirsiniz final, böylece derleyici alt sınıfta (yanlışlıkla) onları geçersiz kılmaya çalışırsanız şikayet eder.
Stefan Winkler

4
Çalıştırılmayan aynı adı taşıyan üst yöntem, kulağa bir JUnit davranışı gibi gelmiyor. Bu, OOP'de temel aşırı sürüşün nasıl çalıştığı gibi görünüyor. Ana yöntem temelde çalışma zamanında mevcut değildir. Çocuk onu tüm niyet ve amaçlar için değiştirir. Java böyle çalışır.
Brandon

1
Bir başka sorun da, üst sınıfların herkese açık olması gerektiğidir, aksi @Beforetakdirde bir alt sınıfın da bir @Beforeyöntemi varsa , işaretlenen yöntemleri göz ardı edilecektir .
rusins

21

Bence dokümantasyona @Beforeve @Afterdoğru sonuca dayanarak , yöntemlere benzersiz adlar vermek. Testlerimde aşağıdaki kalıbı kullanıyorum:

public abstract class AbstractBaseTest {

  @Before
  public final void baseSetUp() { // or any other meaningful name
    System.out.println("AbstractBaseTest.setUp");
  }

  @After
  public final void baseTearDown() { // or any other meaningful name
    System.out.println("AbstractBaseTest.tearDown");
  }
}

ve

public class Test extends AbstractBaseTest {

  @Before
  public void setUp() {
    System.out.println("Test.setUp");
  }

  @After
  public void tearDown() {
    System.out.println("Test.tearDown");
  }

  @Test
  public void test1() throws Exception {
    System.out.println("test1");
  }

  @Test
  public void test2() throws Exception {
    System.out.println("test2");
  }
}

sonuç olarak vermek

AbstractBaseTest.setUp
Test.setUp
test1
Test.tearDown
AbstractBaseTest.tearDown
AbstractBaseTest.setUp
Test.setUp
test2
Test.tearDown
AbstractBaseTest.tearDown

Bu yaklaşımın avantajı: AbstractBaseTest sınıfının kullanıcıları, yanlışlıkla setUp / tearDown yöntemlerini geçersiz kılamaz. Eğer isterlerse, tam adı bilmeleri gerekir ve bunu yapabilirler.

Bu yaklaşımın (küçük) dezavantajı: Kullanıcılar, kurulumlarından / tearDown'larından önce veya sonra olan şeyler olduğunu göremezler. Bunların soyut sınıf tarafından sağlandığını bilmeleri gerekir. Ama soyut sınıfı kullanmalarının sebebinin bu olduğunu varsayıyorum


2
güzel bir örnek - iki @Test yönteminiz olsaydı daha da açıklayıcı olurdu, bu nedenle setUp ve tearDown'ın her test yöntemini sardığı görülebilir .
Mark

Bence bu, OP'ye verilecek en iyi cevabın temeli, ancak tek başına cevabınızı doldurmalısınız. Başkalarının önerdiği alternatifleri de kapsayacak şekilde örneğinizi artırabilir ve teklifinizin neden daha üstün olduğunu açıklayabilir misiniz?
wachr

2

Bir şeyleri tersine çevirirseniz, temel sınıf özetinizi bildirebilir ve alt bileşenlerin temel sınıfın açıklamalı setUp ve tearDown yöntemlerinde çağrılan setUp ve tearDown yöntemlerini (ek açıklamalar olmadan) bildirmesini sağlayabilirsiniz.


1
kötü bir fikir değil, ama kendi kurulum / yıkım gerektirmeyen testler için bir sözleşme uygulamak istemiyorum
Joel

2

Her zaman önce çağrıldığından @BeforeClassemin olmak için ek açıklamayı kullanabilirsiniz setup(). Benzer şekilde, her zaman en son çağrıldığından @AfterClassemin olmak için ek açıklamayı kullanabilirsiniz tearDown().

Bu genellikle tavsiye edilmez, ancak desteklenir .

Bu tam olarak istediğiniz şey değil - ama esasen testlerinizin çalıştığı süre boyunca DB bağlantınızı açık tutacak ve ardından sonunda bir kez ve tamamen kapatacaktır.


2
Bunu olsaydı Aslında, ben bir yöntem oluşturma öneriyoruz setupDB()ve closeDB()ve bunları işaretleme @BeforeClassve @AfterClassve ile yöntemlerden sonra önce / değiştirilmesi setup()vetearDown()
Swati

Açıklamalı @BeforeClassve @AfterClassstatik olması gereken yöntemler . Örnek değişkenleri bu yöntemlerde kullanmak istediğimizde durum ne olacak?
Pratik Singhal

@BeforeClassPowermock ile kullanırken bir uyarı : yalnızca ilk test çalıştırması için çalışır. Bu soruna bakın: github.com/powermock/powermock/issues/398
Dagmar

2

Bu, sloganlı soruya bir cevap değil, ancak sorunun gövdesinde bahsedilen sorunlara bir cevaptır. @Before veya @After kullanmak yerine, size daha fazla esneklik sağladığı için @ org.junit.Rule kullanma konusuna bakın . ExternalResource (4.7 itibariyle), bağlantıları yönetiyorsanız en çok ilgilendiğiniz kuraldır. Ayrıca, kurallarınızın garantili yürütme sırasını istiyorsanız, bir Kural Zinciri kullanın ( 4.10'dan itibaren ). Bu soru sorulduğunda bunların hepsinin mevcut olduğuna inanıyorum. Aşağıdaki kod örneği ExternalResource'un javadocs'tan kopyalanmıştır.

 public static class UsesExternalResource {
  Server myServer= new Server();

  @Rule
  public ExternalResource resource= new ExternalResource() {
      @Override
      protected void before() throws Throwable {
          myServer.connect();
         };

      @Override
      protected void after() {
          myServer.disconnect();
         };
     };

  @Test
  public void testFoo() {
      new Client().run(myServer);
     }
 }
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.