Test yöntemleri JUnit4 belirli bir sırayla nasıl çalıştırılır?


413

@TestBelirli bir sırayla açıklamalı test yöntemlerini yürütmek istiyorum .

Örneğin:

public class MyTest {
    @Test public void test1(){}
    @Test public void test2(){}
}

Her koşuşumdan test1()önce kaçmak istiyorum ama böyle bir açıklama bulamadım .test2()MyTest@Test(order=xx)

JUnit için oldukça önemli bir özellik olduğunu düşünüyorum, eğer JUnit'in yazarı sipariş özelliğini istemiyorsa , neden?


2
Bana kaynak dosyada göründükleri sırayla yürütülüyor gibi geliyor.
Lorne Marquis

84
Asla belirli bir sırada yapılması gereken testleri yazmamalısınız. Bu gerçekten kötü bir uygulama. Her test bağımsız çalışabilmelidir.
Apfelsaft

5
@EJP bu, java pre 7 için neredeyse evrensel olarak doğruydu. 7 öncesi, çoğu JVM bunu yaptı, ancak asla garanti edilmedi. Java 7 JVM'leri yöntemleri belirleyici olmayan bir sırayla döndürebilir.
Matthew Farwell

17
Etrafında çalış. Test durumlarından @Test'i kaldırın, bunları özel işlevler olarak dönüştürün, ardından tek bir test senaryosu oluşturun ve sırayla özel işlevleri çağırın.
Simon Guo

12
Test durumlarından @Test kaldırıldığında JUnit raporu bozulur. Bu arada, belirli bir siparişin uygulanması Birim testleri için kötü bir uygulamadır, ancak Entegrasyon testleri için kötü bir uygulamadır . İyi seçim (değil ideal) ile sınıf açıklama etmektir @FixMethodOrder(MethodSorters.NAME_ASCENDING), tutmak @Test, örneğin tüm test yöntemleri için ek açıklama ve yürütme istenen sıraya bağlı olarak alfabetik yeniden adlandırabilir t1_firstTest(), t2_secondTest()vb
MisterStrickland

Yanıtlar:


238

JUnit için oldukça önemli bir özellik olduğunu düşünüyorum, eğer JUnit'in yazarı sipariş özelliğini istemiyorsa, neden?

JUnit ile bunu yapmak için temiz bir yol olduğundan emin değilim, bence JUnit tüm testler keyfi bir sırayla yapılabilir varsayar. SSS bölümünden:

Test fikstürünü nasıl kullanırım?

(...) Test yöntemi çağrılarının sıralanması garanti edilmez , bu nedenle testOneItemCollection (), testEmptyCollection () öğesinden önce yürütülebilir. (...)

Neden böyle? Test sırasını bağımlı hale getirmenin yazarların tanıtmak istemediği bir uygulama olduğuna inanıyorum . Testler bağımsız olmalıdır, bunlar birleştiğinde edilmemelidir ve bu ihlal edecek bakımı zordur şeyler yapmak, bireysel olarak (tabii ki), vb testler yeteneği kıracak

Bununla birlikte, gerçekten bu yönde gitmek istiyorsanız, TestNG'yi herhangi bir keyfi sırada (ve yöntemleri belirtmek gibi yöntemler yöntem gruplarına bağlı olarak) herhangi bir keyfi sırada çalıştırmayı desteklediğinden, TestNG'yi kullanmayı düşünün. Cedric Beust , testte testlerin yürütülmesi sırasında bunun nasıl yapılacağını açıklar .


14
Ya iki bağımsız testiniz var, ya da sadece bir testiniz var ve böyle kodlamalısınız.
Jon Freedman

2
@JonFreedman, soruyu anladığım gibi, testlerin birbirine bağımlı olması değil, sadece test edilecek şeylere sahip olması ve sonuçların bu sırada görünmesini istemek değil.
Jon Bright

174
Birim testleri için zorlama emrini anlayamıyorum, ancak entegrasyon testleri yazmak için JUnit kullanırken, testlerin yürütülme sırasını belirtmek güzel olurdu. Örn. Önce oturum açma testini çalıştırın.
Brian DiCasa

13
@BrianD. giriş muhtemelen diğerlerinden önce çalışması gereken bir test yerine bir "fikstür" dir. Muhtemelen giriş yapan bir BeforeClass yazacağım ve daha sonra herhangi bir sırayla yürütmek için testleri yazacağım.
marcospereira

47
"Testler bağımsız olmalı => testler SİPARİŞ bağımsız olmalıdır" ifadesi doğru değildir. Öğrencinin ödevlerini otomatik olarak derecelendirmeyi düşünün. Çözümlerini önce daha küçük girdiler için ve daha sonra daha büyük girdiler için test etmek istiyorum. Çözüm daha küçük girişler için başarısız olduğunda (zaman / bellek sınırı için), testler neden daha büyük girişler için çalıştırılsın?
mirelon

96

Junit'in mevcut örneğinizden kurtulur ve oluşturma yolunda JUnit 4.11 veya daha üstünü indirirseniz , aşağıdaki kod test yöntemlerini artan sırayla sıralanmış olarak adlarına göre yürütür:

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class SampleTest {

    @Test
    public void testAcreate() {
        System.out.println("first");
    }
    @Test
    public void testBupdate() {
        System.out.println("second");
    }
    @Test
    public void testCdelete() {
        System.out.println("third");
    }
}

8
Örneğin, benzer bir test_001_login () yöntemini denedik, ancak çoğunlukla siparişi korumak için çalışıyor olsa da, garanti edilmez - 007, 005 ve 006'nın 007'den sonra çalıştırıldığı test çalışması başına birkaç örneğimiz vardır. "WTF !," diyorsunuz ve cevaplar için StackOverflow'a çalıştırıyorsunuz.
Max P Magee

Awesome - JUnit 4.12'de mevcut
DtechNet

1
testlerimde: testAcase - çalıştı, test_A_case / testA_case - olmadı!
Rodislav Moldovan

6
Bu açıklama parametresini "MethodSorters.JVM", örneğin "@FixMethodOrder (MethodSorters.JVM)" denedim. API'den: JVM - Test yöntemlerini JVM tarafından döndürülen sırayla bırakır. Yaptığım şey için iyi çalışıyor (CRUD), test yöntemlerini yazıldıkları sırayla çalıştırıyor. +1
Edvinauskas

1
Bu ek açıklama gerçekten bir cevaptır, ancak bunun (Junit 4.12'de) tanımlanmadığı @Inheritedve bu nedenle AbstractTestCaseebeveyn sınıfımda etkisiz hale geldiği konusunda uyarıcıdır .
AbVog

49

Sipariş önemliyse, siparişi kendiniz yapmalısınız.

@Test public void test1() { ... }
@Test public void test2() { test1(); ... }

Özellikle, gerekirse test etmek için olası tüm sipariş permütasyonlarını listelemelisiniz.

Örneğin,

void test1(); 
void test2(); 
void test3(); 


@Test
public void testOrder1() { test1(); test3(); }

@Test(expected = Exception.class)
public void testOrder2() { test2(); test3(); test1(); }

@Test(expected = NullPointerException.class)
public void testOrder3() { test3(); test1(); test2(); }

Veya, tüm permütasyonların tam bir testi:

@Test
public void testAllOrders() {
    for (Object[] sample: permute(1, 2, 3)) {
        for (Object index: sample) {
            switch (((Integer) index).intValue()) {
                case 1: test1(); break; 
                case 2: test2(); break; 
                case 3: test3(); break; 
            }
        }
    }
}

Burada, permute()tüm olası permütasyonları bir dizi koleksiyonuna yineleyen basit bir işlevdir.


Peki ya farklı dosyalarda testler yapılırsa?
Oleg Abrazhaev

3
İlk kod bloğunda tekrartest2 çalışır . Junit daha önce de koşabilir . Bu muhtemelen istediğiniz gibi değildir ve soruya geçerli bir cevap değildir. test1 test2test1
toolforger

47

TestNG'ye geçiş en iyi yol gibi görünüyor, ancak burada jUnit için net bir çözüm göremiyorum. İşte jUnit için bulduğum en okunabilir çözüm / biçimlendirme :

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class SampleTest {
    @Test
    void stage1_prepareAndTest(){};

    @Test
    void stage2_checkSomething(){};

    @Test
    void stage2_checkSomethingElse(){};

    @Test
    void stage3_thisDependsOnStage2(){};

    @Test
    void callTimeDoesntMatter(){}
}

Bu aşama 2 yöntemlerinin aşama 1 olanlardan sonra ve aşama3 olanlardan önce çağrılmasını sağlar.


5
Bu yaklaşım güzel, ancak 10'dan fazla 0void stage01_prepareAndTest(){ }
testiniz varsa

10'dan fazla aşama varsa (test değil) - Evet. Mümkün olduğunda aşama sayısını sınırlamayı ve her aşamada daha fazla test yaptırmayı tercih ederim.
joro

18

Junit üzerinde çalışırken karşılaştığım ana sorunlardan biri ve benim için iyi çalışan aşağıdaki çözümü buldum:

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;

public class OrderedRunner extends BlockJUnit4ClassRunner {

    public OrderedRunner(Class<?> clazz) throws InitializationError {
        super(clazz);
    }

    @Override
    protected List<FrameworkMethod> computeTestMethods() {
        List<FrameworkMethod> list = super.computeTestMethods();
        List<FrameworkMethod> copy = new ArrayList<FrameworkMethod>(list);
        Collections.sort(copy, new Comparator<FrameworkMethod>() {

            @Override
            public int compare(FrameworkMethod f1, FrameworkMethod f2) {
                Order o1 = f1.getAnnotation(Order.class);
                Order o2 = f2.getAnnotation(Order.class);

                if (o1 == null || o2 == null) {
                    return -1;
                }

                return o1.order() - o2.order();
            }
        });
        return copy;
    }
}

ayrıca aşağıdaki gibi bir arayüz oluşturun:

 @Retention(RetentionPolicy.RUNTIME)


@Target({ ElementType.METHOD})

public @interface Order {
public int order();
}

Şimdi aşağıdaki gibi birkaç test senaryosu yazdığınız A sınıfınız olduğunu varsayalım:

(@runWith=OrderRunner.class)
Class A{
@Test
@Order(order = 1)

void method(){

//do something

}

}

Böylece yürütme "method ()" adlı yöntemden başlayacaktır. Teşekkürler!


PowerMock ile başka bir JUnit Runner'ı kullanma 1.6.0 sürümünden bu yana PowerMock, JUnit Kuralı kullanmadan test yürütmesini başka bir JUnit runner'a devretme desteğine sahiptir. Bu, gerçek test yürütmesini istediğiniz başka bir koşucuya bırakır. @RunWith(PowerMockRunner.class) @PowerMockRunnerDelegate(OrderedRunner.class)
Kyriakos Georgiopoulos

11

JUnit şu anda test yöntemlerinin sınıf ek açıklamalarını kullanarak sipariş çalıştırmasına izin vermektedir:

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@FixMethodOrder(MethodSorters.JVM)
@FixMethodOrder(MethodSorters.DEFAULT)

Varsayılan olarak test yöntemleri alfabetik sırayla çalıştırılır. Bu nedenle, belirli yöntemler sırasını ayarlamak için bunları şöyle adlandırabilirsiniz:

a_TestWorkUnit_WithCertainState_ShouldDoSomething b_TestWorkUnit_WithCertainState_ShouldDoSomething c_TestWorkUnit_WithCertainState_ShouldDoSomething

Örnekleri burada bulabilirsiniz .



6

Bir JUnit raporuna bakın. JUnit zaten pakete göre düzenlenmiştir. Her paketin her biri birden fazla TestCas çalıştıran TestSuite sınıflarına sahiptir (veya sahip olabilir). Her TestCase, formun public void test*()her biri ait oldukları TestCase sınıfının bir örneği olacak birden çok test yöntemine sahip olabilir . Her test yönteminin (TestCase örneği) bir adı ve başarılı / başarısız ölçütü vardır.

Yönetimimin gerektirdiği , her biri kendi başarılı / başarısız kriterlerini bildiren ayrı TestStep öğeleri kavramıdır . Herhangi bir test adımının başarısız olması, sonraki test adımlarının yürütülmesini engellememelidir.

Geçmişte, konumumdaki test geliştiricileri TestCase sınıflarını, test edilen ürünün parçalarına karşılık gelen paketler halinde organize ettiler, her test için bir TestCase sınıfı oluşturdular ve her test yöntemini testte ayrı bir "adım" haline getirdiler, JUnit çıktısında kendi başarılı / başarısız kriterleri ile tamamlanır. Her TestCase bağımsız bir "test" tir, ancak TestCase içindeki ayrı yöntemler veya test "adımları" belirli bir sırada gerçekleşmelidir.

TestCase yöntemleri TestCase'in adımlarıdır ve test tasarımcıları test adımı başına ayrı bir başarılı / başarısız ölçütüne sahiptir. Şimdi test adımları karıştı ve testler (elbette) başarısız oldu.

Örneğin:

Class testStateChanges extends TestCase

public void testCreateObjectPlacesTheObjectInStateA()
public void testTransitionToStateBAndValidateStateB()
public void testTransitionToStateCAndValidateStateC()
public void testTryToDeleteObjectinStateCAndValidateObjectStillExists()
public void testTransitionToStateAAndValidateStateA()
public void testDeleteObjectInStateAAndObjectDoesNotExist()
public void cleanupIfAnythingWentWrong()

Her test yöntemi kendi ayrı başarılı / başarısız kriterlerini belirtir ve raporlar. Sipariş için bunu "tek bir büyük test yöntemi" olarak daraltmak, JUnit özet raporunda her "adım" ın başarılı / başarısız ölçütleri ayrıntı düzeyini kaybeder. ... bu da yöneticilerimi üzüyor. Şu anda başka bir alternatif talep ediyorlar.

Herkes şifreli test yöntemi siparişi ile bir JUnit yukarıda örnek ve yönetimi tarafından gerekli her ayrı test adım ayrı ayrı / başarısız kriterleri nasıl destekleyebilir açıklayabilir?

Dokümantasyondan bağımsız olarak, bunu JUnit çerçevesindeki birçok test geliştiricisi için hayatı zorlaştıran ciddi bir gerileme olarak görüyorum.


6

JUnit 5 güncellemesi (ve benim düşüncem)

JUnit için oldukça önemli bir özellik olduğunu düşünüyorum, eğer JUnit'in yazarı sipariş özelliğini istemiyorsa, neden?

Varsayılan olarak, birim sınama kitaplıkları sınamaları kaynak dosyasında gerçekleşen sırayla yürütmeye çalışmaz.
JUnit 5 olarak JUnit 4 bu şekilde çalışır. Neden ? Çünkü sipariş önemliyse, bazı testlerin bunlar arasında bağlandığı ve birim testler için istenmeyen olduğu anlamına gelir .
Böylece @NestedJUnit 5 tarafından sunulan özellik aynı varsayılan yaklaşımı izler.

Ancak entegrasyon testleri için, test yönteminin sırası önemli olabilir, çünkü bir test yöntemi uygulamanın durumunu başka bir test yöntemi tarafından beklenen şekilde değiştirebilir. Örneğin, bir e-mağaza ödeme işlemi için bir entegrasyon testi yazdığınızda, yürütülecek ilk test yöntemi bir istemciyi kaydettirmek, ikincisi sepete öğeler eklemek ve sonuncusu ödeme yapmaktır. Test koşucusu bu sıraya uymazsa, test senaryosu kusurludur ve başarısız olur.
JUnit 5'te (5.4 sürümünden), test sınıfına açıklama @TestMethodOrder(OrderAnnotation.class)ekleyerek @Order(numericOrderValue)ve siparişin önemli olduğu yöntemler için sipariş belirterek yürütme sırasını ayarlama olanağına sahip olursunuz .

Örneğin :

@TestMethodOrder(OrderAnnotation.class) 
class FooTest {

    @Order(3)
    @Test
    void checkoutOrder() {
        System.out.println("checkoutOrder");
    }

    @Order(2)
    @Test
    void addItemsInBasket() {
        System.out.println("addItemsInBasket");
    }

    @Order(1)
    @Test
    void createUserAndLogin() {
        System.out.println("createUserAndLogin");
    }
}

Çıktı :

createUserAndLogin

addItemsInBasket

checkoutOrder

Bu arada, belirtmek @TestMethodOrder(OrderAnnotation.class)gerekli değil gibi görünüyor (en azından test ettiğim 5.4.0 sürümünde).

Yan not
Soru hakkında: JUnit 5 entegrasyon testleri yazmak için en iyi seçim mi? Düşünmek için ilk araç olması gerektiğini düşünmüyorum (Salatalık ve ortak entegrasyon testleri için daha spesifik değer ve özellikler getirebilir), ancak bazı entegrasyon testi vakalarında, JUnit çerçevesi yeterlidir. Bu, özelliğin var olduğu iyi bir haber.


4

Kabul etmem emin değilim, 'Dosya Yükleme'yi test edip' Dosya Yükleme ile Eklenen Verileri 'test etmek istersem neden bunların birbirinden bağımsız olmasını istemeyeyim? Her ikisi de bir Goliath test senaryosuna sahip olmak yerine onları ayrı ayrı çalıştırabileceğimi düşünüyorum.


3

Test senaryoları bir paket olarak çalıştırıldığında istediğiniz her şey makul.

Ne yazık ki şu anda tam bir çözüm vermek için zaman yok, ancak sınıfa bir göz atın:

org.junit.runners.Suite

Bu, test senaryolarını (herhangi bir test sınıfından) belirli bir sırayla çağırmanızı sağlar.

Bunlar işlevsel, entegrasyon veya sistem testleri oluşturmak için kullanılabilir.

Bu, ünite testlerinizi, ister böyle isteseniz de çalıştırsın, belirli bir düzen olmadan (önerilen şekilde) olduğu gibi bırakır ve daha büyük bir resmin parçası olarak testleri tekrar kullanır.

Birim, entegrasyon ve sistem testleri için bazen aynı zamanda veri odaklı, bazen taahhütlü ve bazen de paket olarak çalışan aynı kodu yeniden kullanır / devralırız.


2
Bu cevabı 2014'ten beri tamamlamak için vaktiniz yok mu? ;)
Charlie

2

Çözümümü buradan görebilirsiniz: "Junit ve java 7."

Bu makalede, "kaynak kodunuzda olduğu gibi" sırayla junit testlerinin nasıl yapılacağını açıklarım. Test yöntemleriniz sınıf dosyasında göründüğü gibi testler yapılacaktır.

http://intellijava.blogspot.com/2012/05/junit-and-java-7.html

Ancak Pascal Thivent'in dediği gibi, bu iyi bir uygulama değil.


Blog yayınınızı gördüm (Rusça!), Ama bu çok karmaşık.
Nicolas Barbulesco

1
@NicolasBarbulesco İki blogum var (rus ve eng). Çok karmaşık çünkü yürütme sırası bağımlılığına sahip testler oluşturmayacaksınız. Benim çözümüm geçici çözüm, ama gerçek çözüm - bu bağımlılığı ortadan kaldırmak.
kornero

1
Bu gönderi gerçek bir cevap içermiyor. Lütfen, bağlantının yanı sıra en azından temel açıklamayı da eklemeyi düşünün.
varsayılan yerel ayar


0

Birkaç cevap okudum ve en iyi uygulama olmadığını kabul ediyorum, ancak testlerinizi sipariş etmenin en kolay yolu - ve JUnit'in testleri varsayılan olarak çalıştırma şekli, alfabetik adın artmasıdır.

Bu yüzden testlerinizi istediğiniz alfabetik sırayla adlandırın. Ayrıca test adının test kelimesi ile başlaması gerektiğini unutmayın. Sadece sayılara dikkat et

test12 test2'den önce çalışır

yani:

testA_MyFirstTest testiC_ThirdTest testiB_ATestThatRunsSecond


0

Lütfen buna bir göz atın : https://github.com/TransparentMarket/junit . Testi belirtildikleri sırayla çalıştırır (derlenmiş sınıf dosyasında tanımlanır). Ayrıca, önce alt paket tarafından tanımlanan testleri çalıştırmak için bir AllTests paketi içerir. AllTests uygulamasını kullanarak çözüm, özelliklerin filtrelenmesinde de genişletilebilir (@Fast ek açıklamalarını kullandık, ancak bunlar henüz yayınlanmadı).


0

JUnit için istenen davranışı üretebilecek bir uzantı aşağıdadır: https://github.com/aafuks/aaf-junit

Bunun JUnit felsefesinin yazarlarına karşı olduğunu biliyorum, ancak JUnit'i katı birim testi olmayan ortamlarda (Java'da uygulandığı gibi) kullanırken bu çok yararlı olabilir.


0

Buraya, testlerimin sırayla yapılmadığını düşünerek bitirdim, ama gerçek şu ki, karmaşa benim asenkron işlerimdeydi. Eşzamanlılık ile çalışırken, testleriniz arasında eşzamanlılık kontrolleri yapmanız gerekir. Benim durumumda, işler ve testler bir semafor paylaşıyor, bu yüzden sonraki testler çalışan iş kilidi serbest bırakana kadar askıda kalıyor.

Bunun bu soru ile tamamen ilgili olmadığını biliyorum, ancak doğru sorunu hedeflemeye yardımcı olabilir


0

şu kod parçalarından birini kullanabilirsiniz:

@FixMethodOrder(MethodSorters.JVM)OR `@FixMethodOrder(MethodSorters.DEFAULT)` OR `@FixMethodOrder(MethodSorters.NAME_ASCENDING)` before your test class like this:


@FixMethodOrder(MethodSorters.NAME_ASCENDING)


public class BookTest { ...}
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.