获取JUnit 4中当前正在执行的测试的名称


240

在JUnit 3中,我可以这样获得当前正在运行的测试的名称:

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

它将显示“当前测试为testSomething”。

JUnit 4中是否有任何现成的或简单的方法可以做到这一点?

背景:显然,我不想只打印测试的名称。我要加载存储在与测试名称相同的资源中的特定于测试的数据。您知道,约定优于配置以及所有这些。


上面的代码在JUnit 4中为您提供了什么?
比尔蜥蜴

5
JUnit 3测试扩展了TestCase,其中定义了getName()。JUnit 4测试不扩展基类,因此根本没有getName()方法。
戴夫·雷

我有一个类似的问题,我想<b>设置</ b>测试名称,因为我使用的是Parametrized运行器,该运行器仅给我编号的测试用例。
Volker Stolz

使用Test或TestWatcher的可爱解决方案...只是想(大声)怀疑是否应该这样做?您可以通过查看Gradle提供的时序输出图表来确定测试是否运行缓慢。您永远不需要知道测试的顺序...?
mike啮齿动物

Answers:


378

JUnit 4.7似乎使用TestName-Rule添加了此功能。看起来这将为您获取方法名称:

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
另外请注意,测试名不@Before提供:(参见:old.nabble.com/...
JM。

41
显然JUnit的较新版本执行@Rule之前,@Before我是新来的JUnit,并根据- TestName在我@Before没有任何困难。
MightyE


2
如果您使用的是参数化测试,则“ name.getMethodName()”将返回{testA [0],testA [1]等},因此我使用类似:assertTrue(name.getMethodName()。matches(“ testA(\ [\ \ d \])?“));
莱格娜(Legna)2014年

2
@DuncanJones为什么建议的替代方案“更有效”?
斯蒂芬

117

JUnit 4.9.x及更高版本

从JUnit 4.9开始,TestWatchman不推荐使用TestWatcher该类,而推荐使用该类:

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

注意:必须声明包含类public

JUnit 4.7.x-4.8.x

以下方法将打印类中所有测试的方法名称:

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

1
@takacsot令人惊讶。您能否对此发表一个新的问题,然后在此处向我发送链接?
Duncan Jones

为什么要使用public字段?
拉菲·哈查杜安


16

JUnit 5及更高版本

JUnit 5中,您可以注入TestInfo,从而简化了提供给测试方法的测试元数据。例如:

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

查看更多:JUnit 5用户指南TestInfo javadoc


9

尝试以下方法:

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());
        }
}

输出看起来像这样:

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

注:此DOES NOT如果你的测试的子类工作的TestCase!测试运行,但是@Rule代码永远不会运行。


3
在这个例子中,上帝保佑你的笔记。
user655419 2013年

“这不起作用”-例如-黄瓜忽略@Rule注释
Benjineer 2015年

8

考虑使用SLF4J(Java简单日志记录门面)使用参数化消息提供了一些巧妙的改进。将SLF4J与JUnit 4规则实现相结合可以提供更有效的测试类日志记录技术。

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

一种复杂的方法是通过将org.junit.runners.BlockJUnit4ClassRunner子类化来创建自己的Runner。

然后,您可以执行以下操作:

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

然后,对于每个测试类,您需要添加一个@RunWith(NameAwareRunner.class)批注。另外,如果您不想每次都记住它,可以将其放在Test超类上。当然,这限制了您对跑步者的选择,但这是可以接受的。

另外,可能需要一点功夫才能将当前测试名称从Runner中获取并进入您的框架,但这至少会使您获得该名称。


至少从概念上讲,这个想法对我而言似乎很简单。我的观点是:我不会称它为混乱。
user98761

“在测试超类上……”-请不要再使用基于可怕的继承的设计模式了。JUnit3就是这样!

3

JUnit 4没有任何开箱即用的机制来让测试用例获得自己的名称(包括在安装和拆卸期间)。


1
除了检查堆栈之外,是否还有一种非现成的机制?
戴夫·雷

4
鉴于以下答案,情况并非如此!也许将正确答案分配给其他人?
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
我可以争辩说,他只想展示一个解决方案..看不出为什么投票否决.... @downvoter:至少,至少要添加有用的信息。–
Victor

1
@skaffman我们都喜欢看到各种替代解决方案。这是我正在寻找的最接近的名称:不是直接在testclass中而是在测试期间使用的类中获取测试名称(例如,在记录器组件中的某个位置)。在那里,与测试相关的注释不再起作用。
Daniel Alder

3

根据前面的评论,并进一步考虑,我创建了TestWather的扩展,您可以通过以下方式在JUnit测试方法中使用它:

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

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

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

下一个测试帮助程序类:

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());
}
}

请享用!


嗨,那是什么ImportUtilsTest,我收到一个错误,似乎是一个记录器类,我是否有更多信息?谢谢
Sylhare

1
命名的类只是JUnit测试类的一个示例:JUnitHelper的用户。我将更正用法示例。
Csaba Tenkes

啊,现在我觉得很蠢,这是如此明显。非常感谢!;)
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

我建议您将测试方法名称与测试数据集分离。我将对一个DataLoaderFactory类建模,该类从您的资源中加载/缓存测试数据集,然后在您的测试用例cam中调用一些接口方法,该方法为测试用例返回一组测试数据。将测试数据与测试方法名称相关联时,假设测试数据只能使用一次,在大多数情况下,我建议在多个测试中使用相同的测试数据来验证业务逻辑的各个方面。


0

您可以使用Slf4j和实现TestWatcher

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中,TestInfo它可以替代JUnit 4中的TestName规则。

从文档中:

TestInfo用于将有关当前测试或容器的信息注入到@ Test,@ RepeatedTest,@ ParameterizedTest,@ TestFactory,@ BeforeEach,@ AfterEach,@ BeforeAll和@AfterAll方法中。

要检索当前执行的测试的方法名称,您有两个选项: String TestInfo.getDisplayName()Method TestInfo.getTestMethod()

仅检索当前测试方法的名称 TestInfo.getDisplayName()可能不够,因为测试方法的默认显示名称为methodName(TypeArg1, TypeArg2, ... TypeArg3)
在中复制方法名称@DisplayName("..")不一定是一个好主意。

作为替代,您可以使用 TestInfo.getTestMethod()返回一个Optional<Method>对象。
如果在测试方法内部使用了检索方法,则甚至不需要测试Optional包装的值。

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的JUnit 5

优点:

您可以ExtensionContext通过覆盖获得的附加功能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();
    }

}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.