Soru Birden çok test dersi arasında JUnit BeforeClass mantığı nasıl paylaşılır


Şu anda, tüm JUnit testlerimle etiketlenmiş yöntemler sağlayan ortak bir temel sınıftan @BeforeClass ve @AfterClass ek açıklamalar - tüm bunlar gerçekten kullanmanız gereken testler için bir dizi statik kaynak / hizmet kuruyor.

Bu birkaç nedenden ötürü bana garip geliyor:

  1. JUnit4'ün (benim anlayışımdan) bir parçası, artık bu klasik test mirasına ihtiyacımız olmaması gerektiğidir.
  2. Bu testleri tek tek yerine bir paketin parçası olarak çalıştırdığımda (ki bunu sıklıkla yapıyoruz) @BeforeClass ve @AfterClass Birden fazla kez çağırmak, testleri yavaşlatmak - gerçekten bunları sadece bir kez aramalıyız

Benim yapmak istediğim şey, şimdiki BeforeClass / AfterClass mantığını miras zincirinden ve bireysel testler ve bir bütün olarak süit tarafından paylaşılabilecek bir şeye taşımaktır.

Bu yapılabilir mi? Öyleyse nasıl?  (Önemliyse, JUnit 4.7 kullanıyorum ve farklı bir sürüme güncellemek zor bir satış olabilir)


18
2017-12-09 23:51


Menşei




Cevaplar:


İlk sayının çözümü, mantığı bir uzantıya taşımaktır. org.junit.rules.ExternalResource test yoluyla bir @ClassRule, JUnit 4.9'da tanıtıldı:

public class MyTest {
    @ClassRule
    public static final TestResources res = new TestResources();
    @Test
    public void testFoo() {
        // test logic here
    }
}

public class TestResources extends ExternalResource {
    protected void before() {
        // Setup logic that used to be in @BeforeClass
    }
    protected void after() {
        // Setup logic that used to be in @AfterClass
    }
}

Bu şekilde, daha önce ana sınıf tarafından yönetilen kaynaklar, bir sınıf çalıştıktan ve bir sınıf çalıştıktan sonra imha edilmeden önce oluşturulabilen daha modüler / tüketilebilir "kaynaklar" içine, test sınıfı hiyerarşisinden çıkarılır.

Her iki konunun aynı zamanda çözülmesinde olduğu gibi - yani: aynı yüksek seviye kurulumun / teardown'un hem bireysel testin bir parçası hem de bir paketin parçası olarak çalışması - bunun için herhangi bir spesifik destek bulunmuyor. . Ancak..., sen kendin uygulayabilirsin:

Sadece değiştirmek @ClassRule Kaynak oluşturmayı / yok etmemeyi belirlemek için dahili olarak sayım yapan bir fabrika modeline kaynak yaratma.

Örneğin (lütfen bunun kaba olduğunu ve sağlamlık için bazı düzeltmelere / hata işlemlerine ihtiyaç duyabileceğini unutmayın):

public class TestResources extends ExternalResource {
    private static int refCount = 0;

    private static TestResources currentInstance;

    public static TestResources getTestResources () {
        if (refCount == 0) {
            // currentInstance either hasn't been created yet, or after was called on it - create a new one
            currentInstance = new TestResources();
        }
        return currentInstance;
    }

    private TestResources() {
        System.out.println("TestResources construction");
        // setup any instance vars
    }

    protected void before() {
        System.out.println("TestResources before");
        try {
            if (refCount == 0) {
                System.out.println("Do actual TestResources init");
            }
        }
        finally {
            refCount++;
        }
    }

    protected void after() {
        System.out.println("TestResources after");
        refCount--;
        if (refCount == 0) {
            System.out.println("Do actual TestResources destroy");
        }
    }
}

Her iki paketiniz / test sınıfınız sadece kaynağı @ClassResource fabrika yöntemiyle:

@RunWith(Suite.class)
@SuiteClasses({FooTest.class, BarTest.class})
public class MySuite {
    @ClassRule
    public static TestResources res = TestResources.getTestResources();
    @BeforeClass
    public static void suiteSetup() {
        System.out.println("Suite setup");
    }
    @AfterClass
    public static void suiteTeardown() {
        System.out.println("Suite teardown");
    }
}
public class FooTest {
    @ClassRule
    public static TestResources res = TestResources.getTestResources();

    @Test
    public void testFoo() {
        System.out.println("testFoo");
    }
}
public class BarTest {
    @ClassRule
    public static TestResources res = TestResources.getTestResources();

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

Bireysel testi çalıştırırken, refcounting'in herhangi bir etkisi olmayacaktır - "gerçek init" ve "gerçek teardown" sadece bir kez gerçekleşecektir. Süitten geçerken, süit TestResource'u oluşturacak ve bireysel testler zaten eski haline getirilmiş olanı yeniden kullanacaktır (refcounting, onu gerçekten tahrip edilmekten ve takımdaki testler arasında yeniden yaratılmasından korur).


23
2017-12-19 18:40



RefCount sisteminizin çalışmadığını düşünüyorum. Her sınıftan sonra sıfıra düşer ve kaynağınızı siler ve daha sonra getTestResources () öğesini çağırırken bir sonraki sınıfta yeniden başlatır. Bir şeyleri kaçırıyor olabilirim, yanılıyorsam beni düzeltin. - FredBoutin
Fikir, bireysel bir test veya bir test paketi çalıştırırken başlatılabilmesidir. Süit olarak çalışırkenrefCount, süit tarafından 1 olarak ayarlanmıştır. ClassRule. Her bir test refCount değerini 2 olarak değiştirir, ardından tamamlandığında 1'e döner. Süit bittiğinde, 0'a gidecektir. Test olarak çalışırkenTest tamamlandığında, refCount bireysel testle 1, sonra 0'a ayarlanır. Asıl nokta, kaynağın her iki koşuda da bir kez yaratılıp yok edilmesidir. - Krease
Çok net teşekkürler @Krease! - FredBoutin


Kullanabilirsiniz @BeforeClass ve @AfterClass  SUITE SINIFI.

Bu, tüm test sınıfları tamamlandıktan sonra (sırasıyla) ve test sınıflarının herhangi birinin test edilmesinden önce çalışır.

Bu sayede sadece bir kez çalıştırabilirsiniz.

//..usual @RunWith etc annotations here
public class MySuite{

@BeforeClass
public static void setup(){

}

@AfterClass
public static void tearDown(){

}

}

1
2017-12-10 01:41



Bu testler sadece bir suit sınıfından geçiyor olsam iyi olur, ben testi tek tek çalıştırdığımda işe yaramaz (yani: Eclipse'de, sınava sağ tıklayıp "JUnit testi olarak çalıştır" ı seçin) - suit testleri biliyor, ama testler paketi hakkında bilmiyor ... - Krease


Ben benzer bir sorunla karşılaştım (Bahar bir seçenek değildi ve maven projelerinde TestSuites yazmam) bu yüzden bu sorunu çözmek için basit bir koşucu yazdım.

SharedResource sınıfını yazmanız ve sınamayı gerektirecek şekilde sınamanızı işaretlemeniz gerekir.

public class SampleSharedResource implements SharedResource {
public void initialize() throws Exception {
    //init your resource
}

}

@RunWith(JUnitSharedResourceRunner.class)
@JUnitSharedResourceRunner.WithSharedResources({SampleSharedResource.class})
public class SharedResourceRunnerATest {
...

Kaynakları https://github.com/eanlr/junit-shared-resources-runner


1
2017-10-14 11:13