JUnit 4'te yürütülmekte olan testin adını alın


240

JUnit 3'te, şu anda çalışan testin adını şu şekilde alabilirim:

public class MyTest extends TestCase
{
    public void testSomething()
    {
        System.out.println("Current test is " + getName());
        ...
    }
}

"Geçerli test testSomething" yazdıracaktır.

JUnit 4'te hazır veya basit bir yolu var mı?

Arka plan: Açıkçası, sadece testin adını yazdırmak istemiyorum. Sınama ile aynı ada sahip bir kaynakta depolanan sınama özgü verileri yüklemek istiyorum. Bilirsiniz, yapılandırma konvansiyonu ve hepsi.


Yukarıdaki kod JUnit 4'te size ne veriyor?
Kertenkele Bill

5
JUnit 3 testleri getName () tanımlanmış TestCase genişletir. JUnit 4 testleri bir taban sınıfı genişletmez, bu nedenle hiç getName () yöntemi yoktur.
Dave Ray

Ben sadece bana numaralı test durumlarda veren Parametreli runner kullandığım için test adı <b> ayarlamak </b> istiyorum benzer bir sorun var.
Volker Stolz

Test veya TestWatcher kullanarak güzel bir çözüm ... sadece bir ihtiyaç olup olmadığını merak (yüksek sesle)? Gradle tarafından verilen zamanlama çıktı grafiklerine bakarak bir testin yavaş çalışıp çalışmadığını bulabilirsiniz. Testlerin hangi sırayla gerçekleştiğini asla bilmenize gerek yok ...?
mike kemirgen

Yanıtlar:


378

JUnit 4.7 TestName-Rule kullanarak görünüyor bu özelliği ekledi . Bunun size yöntem adını getireceği anlaşılıyor:

import org.junit.Rule;

public class NameRuleTest {
    @Rule public TestName name = new TestName();

    @Test public void testA() {
        assertEquals("testA", name.getMethodName());
    }

    @Test public void testB() {
        assertEquals("testB", name.getMethodName());
    }
}

4
Ayrıca TestName'in @before :( içinde mevcut olmadığını unutmayın. Bkz: old.nabble.com/…
jm.

41
Görünüşe JUnit yeni sürümleri yürütmek @Ruleönce @Beforeben JUnit yeniyim ve bağlı olduğu - TestNamegözlerimi de @Beforeherhangi bir sorun olmadan.
MightyE

9
Bunu mümkün kılmanın daha etkili yolları vardır .
Duncan Jones

2
Eğer parametreleştirilmiş testler kullanıyorsanız "name.getMethodName ()" {testA [0], testA [1] vb.) Döndürecektir. \ d \]) "));
Legna

2
@DuncanJones Önerilen alternatif neden "daha verimli"?
Stephan

117

JUnit 4.9.x ve üstü

JUnit 4.9'dan bu yana TestWatchman, TestWatchersınıf, çağrışımı olan sınıf lehine kullanımdan kaldırılmıştır :

@Rule
public TestRule watcher = new TestWatcher() {
   protected void starting(Description description) {
      System.out.println("Starting test: " + description.getMethodName());
   }
};

Not: Kapsayıcı sınıf bildirilmelidir public.

JUnit 4.7.x - 4.8.x

Aşağıdaki yaklaşım, bir sınıftaki tüm testler için yöntem adlarını yazdırır:

@Rule
public MethodRule watchman = new TestWatchman() {
   public void starting(FrameworkMethod method) {
      System.out.println("Starting test: " + method.getName());
   }
};

1
@takacsot Bu şaşırtıcı. Bununla ilgili yeni bir soru gönderebilir ve bana buradaki bağlantıya ping atabilir misiniz?
Duncan Jones

Neden bir publicalan kullanılmalı?
Raffi Khatchadourian


16

JUnit 5 ve üstü

In JUnit 5 enjekte edebilir TestInfotest yöntemlerine sağlayan hangi basitleştirir testi meta veri. Örneğin:

@Test
@DisplayName("This is my test")
@Tag("It is my tag")
void test1(TestInfo testInfo) {
    assertEquals("This is my test", testInfo.getDisplayName());
    assertTrue(testInfo.getTags().contains("It is my tag"));
}

Daha fazla bilgi için: JUnit 5 Kullanım kılavuzu , TestInfo javadoc .


9

Bunun yerine şunu deneyin:

public class MyTest {
        @Rule
        public TestName testName = new TestName();

        @Rule
        public TestWatcher testWatcher = new TestWatcher() {
            @Override
            protected void starting(final Description description) {
                String methodName = description.getMethodName();
                String className = description.getClassName();
                className = className.substring(className.lastIndexOf('.') + 1);
                System.err.println("Starting JUnit-test: " + className + " " + methodName);
            }
        };

        @Test
        public void testA() {
                assertEquals("testA", testName.getMethodName());
        }

        @Test
        public void testB() {
                assertEquals("testB", testName.getMethodName());
        }
}

Çıktı şöyle görünür:

Starting JUnit-test: MyTest testA
Starting JUnit-test: MyTest testB

NOT: Bu ETMEZ test bir alt sınıf ise çalışmak TestCase ! Test çalışır, ancak @ Kural kodu hiçbir zaman çalışmaz .


3
Tanrı, örneğin örneğinde sizi NOT'unuz için kutsasın.
user655419

"Bu işe yaramaz" - durumda - salatalık @ Kural ek açıklamalarını yoksayar
Benjineer

8

SLF4J (Java için Simple Logging Facade) kullanarak parametreli mesajlar kullanarak bazı düzenli iyileştirmeler sağlar. SLF4J ile JUnit 4 kural uygulamalarını birleştirmek daha verimli test sınıfı günlük kaydı teknikleri sağlayabilir.

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.MethodRule;
import org.junit.rules.TestWatchman;
import org.junit.runners.model.FrameworkMethod;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LoggingTest {

  @Rule public MethodRule watchman = new TestWatchman() {
    public void starting(FrameworkMethod method) {
      logger.info("{} being run...", method.getName());
    }
  };

  final Logger logger =
    LoggerFactory.getLogger(LoggingTest.class);

  @Test
  public void testA() {

  }

  @Test
  public void testB() {

  }
}

6

Kıvrımlı bir yol, org.junit.runners.BlockJUnit4ClassRunner alt sınıfını oluşturarak kendi Runner'ınızı oluşturmaktır.

Daha sonra böyle bir şey yapabilirsiniz:

public class NameAwareRunner extends BlockJUnit4ClassRunner {

    public NameAwareRunner(Class<?> aClass) throws InitializationError {
        super(aClass);
    }

    @Override
    protected Statement methodBlock(FrameworkMethod frameworkMethod) {
        System.err.println(frameworkMethod.getName());
        return super.methodBlock(frameworkMethod);
    }
}

Daha sonra her test sınıfı için bir @RunWith (NameAwareRunner.class) ek açıklaması eklemeniz gerekir. Alternatif olarak, her seferinde hatırlamak istemiyorsanız bu ek açıklamayı Test süper sınıfına koyabilirsiniz. Bu, elbette, koşucu seçiminizi sınırlar, ancak bu kabul edilebilir.

Ayrıca, geçerli test adını Runner'dan ve çerçevenize almak için biraz kung fu gerekebilir, ancak bu en azından adı alır.


En azından kavramsal olarak, bu fikir benim için oldukça basit görünüyor. Demek istediğim şu: Ben buna kıvrık demezdim.
user98761

"Test süper sınıfında ..." - Lütfen, artık korkunç kalıtım tabanlı tasarım modellerine son. Bu çok JUnit3!
oberlies

3

JUnit 4, bir test senaryosunun kendi adını alması için hazır kurulum mekanizması içermez (kurulum ve sökme dahil).


1
Dışarıda yığını incelemek dışında kullanıma hazır bir mekanizma var mı?
Dave Ray

4
Aşağıdaki cevaplar verilmiş değil! belki başka birine doğru cevabı atayabilir mi?
Toby

3
String testName = null;
StackTraceElement[] trace = Thread.currentThread().getStackTrace();
for (int i = trace.length - 1; i > 0; --i) {
    StackTraceElement ste = trace[i];
    try {
        Class<?> cls = Class.forName(ste.getClassName());
        Method method = cls.getDeclaredMethod(ste.getMethodName());
        Test annotation = method.getAnnotation(Test.class);
        if (annotation != null) {
            testName = ste.getClassName() + "." + ste.getMethodName();
            break;
        }
    } catch (ClassNotFoundException e) {
    } catch (NoSuchMethodException e) {
    } catch (SecurityException e) {
    }
}

1
Ben sadece bir çözüm göstermek istediğini iddia edebilir .. neden olumsuz oy görmüyorum .... @downvoter: en azından, en azından, yararlı bilgiler ekleyin ..
Victor

1
@skaffman Hepimiz alternatif çözümlerin tamamını görmeyi çok seviyoruz. Bu aradığım şey için en yakın olanı: Test adını doğrudan test sınıfında değil, test sırasında kullanılan sınıfta almak (örneğin, bir günlükçü bileşeninde bir yerde). Burada, testle ilgili ek açıklamalar artık çalışmıyor.
Daniel Alder

3

Önceki yoruma dayanarak ve daha da dikkate alınarak ben bu ile JUnit test yöntemlerinde kullanabileceğiniz bir TestWather uzantısı oluşturdu:

public class ImportUtilsTest {
    private static final Logger LOGGER = Logger.getLogger(ImportUtilsTest.class);

    @Rule
    public TestWatcher testWatcher = new JUnitHelper(LOGGER);

    @Test
    public test1(){
    ...
    }
}

Test yardımcısı sınıfı bir sonraki:

public class JUnitHelper extends TestWatcher {
private Logger LOGGER;

public JUnitHelper(Logger LOGGER) {
    this.LOGGER = LOGGER;
}

@Override
protected void starting(final Description description) {
    LOGGER.info("STARTED " + description.getMethodName());
}

@Override
protected void succeeded(Description description) {
    LOGGER.info("SUCCESSFUL " + description.getMethodName());
}

@Override
protected void failed(Throwable e, Description description) {
    LOGGER.error("FAILURE " + description.getMethodName());
}
}

Zevk almak!


Merhaba bu nedir, ImportUtilsTestbir hata alıyorum, bir logger sınıfı gibi görünüyor, daha fazla bilgi var mı? Teşekkürler
Sylhare

1
Adlandırılmış sınıf, yalnızca bir JUnit test sınıfına örnektir: JUnitHelper kullanıcısı. Kullanım örneğini düzeltirim.
Csaba Tenkes

Ah şimdi aptal hissediyorum, çok açıktı. Çok teşekkürler! ;)
Sylhare

1
@ClassRule
public static TestRule watchman = new TestWatcher() {
    @Override
    protected void starting( final Description description ) {
        String mN = description.getMethodName();
        if ( mN == null ) {
            mN = "setUpBeforeClass..";
        }

        final String s = StringTools.toString( "starting..JUnit-Test: %s.%s", description.getClassName(), mN );
        System.err.println( s );
    }
};

0

Test yöntemi adını test veri kümenizden ayırmanızı öneririm. Kaynaklarınızdaki test verisi kümelerini yükleyen / önbelleğe alan bir DataLoaderFactory sınıfını modelleyeceğim ve test durumunuzda, test durumu için test verisi kümesi döndüren bazı arabirim yöntemini çağırın. Test verilerinin test yöntemi adına bağlı olması, test verilerinin yalnızca bir kez kullanılabileceğini varsayar; burada çoğu durumda, iş mantığınızın çeşitli yönlerini doğrulamak için birden fazla testte aynı test verilerinin kullanılmasını öneririm.


0

Bunu kullanarak Slf4jveTestWatcher

private static Logger _log = LoggerFactory.getLogger(SampleTest.class.getName());

@Rule
public TestWatcher watchman = new TestWatcher() {
    @Override
    public void starting(final Description method) {
        _log.info("being run..." + method.getMethodName());
    }
};

0

JUnit 5'te TestInfo, JUnit 4'ten TestName kuralı için bir yedek değiştirme görevi görür.

Belgelerden:

TestInfo, geçerli test veya kapsayıcıyla ilgili bilgileri @Test, @RepeatedTest, @ParameterizedTest, @TestFactory, @BeforeEach, @AfterEach, @BeforeAll ve @AfterAll yöntemlerine enjekte etmek için kullanılır.

Yürütülen geçerli sınamanın yöntem adını almak için iki seçeneğiniz vardır: String TestInfo.getDisplayName()ve Method TestInfo.getTestMethod().

TestInfo.getDisplayName()Test yöntemi varsayılan görünen adı olduğundan, yalnızca geçerli test yönteminin adını almak yeterli olmayabilir methodName(TypeArg1, TypeArg2, ... TypeArg3).
Yöntem adlarını çoğaltma@DisplayName("..") iyi bir fikir değildir.

Alternatif olarak TestInfo.getTestMethod(), bir Optional<Method>nesne döndüren kullanabilirsiniz .
Alma yöntemi bir test yönteminin içinde kullanılıyorsa, Optionalsarılmış değeri test etmeniz bile gerekmez .

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.TestInfo;
import org.junit.jupiter.api.Test;

@Test
void doThat(TestInfo testInfo) throws Exception {
    Assertions.assertEquals("doThat(TestInfo)",testInfo.getDisplayName());
    Assertions.assertEquals("doThat",testInfo.getTestMethod().get().getName());
}

0

ExtensionContext aracılığıyla JUnit 5

Avantajı:

ExtensionContextGeçersiz kılmanın ek işlevlerine sahip olursunuz afterEach(ExtensionContext context).

public abstract class BaseTest {

    protected WebDriver driver;

    @RegisterExtension
    AfterEachExtension afterEachExtension = new AfterEachExtension();

    @BeforeEach
    public void beforeEach() {
        // Initialise driver
    }

    @AfterEach
    public void afterEach() {
        afterEachExtension.setDriver(driver);
    }

}
public class AfterEachExtension implements AfterEachCallback {

    private WebDriver driver;

    public void setDriver(WebDriver driver) {
        this.driver = driver;
    }

    @Override
    public void afterEach(ExtensionContext context) {
        String testMethodName = context.getTestMethod().orElseThrow().getName();
        // Attach test steps, attach scsreenshots on failure only, etc.
        driver.quit();
    }

}
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.