首页 文章

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

提问于
浏览
212

在JUnit 3中,我可以得到当前运行的测试的名称,如下所示:

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

这将打印“当前测试是testSomething” .

在JUnit 4中是否有任何开箱即用或简单的方法可以做到这一点?

背景:显然,我不想只打印测试的名称 . 我想加载存储在与测试同名的资源中的特定于测试的数据 . 你知道,convention over configuration等等 .

13 回答

  • 7

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

  • 1

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

    您可以使用 Slf4jTestWatcher 实现此目的

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

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

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

    考虑使用SLF4J(Simple Logging Facade for 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() {
    
      }
    }
    
  • 2

    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 User guideTestInfo javadoc .

  • 0

    一种令人费解的方法是通过继承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和您的框架中获取,但这至少会让您获得名称 .

  • 3

    试试这个:

    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
    

    注意:如果您的测试是 TestCase 的子类,则此 DOES NOT 工作!测试运行但 @Rule 代码从未运行过 .

  • 101

    JUnit 4没有任何开箱即用的机制,可以让测试用例得到它自己的名字(包括在安装和拆卸过程中) .

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

    请享用!

  • 7
    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) {
        }
    }
    
  • 0
    @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

    在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("..") 中复制方法名称不是一个好主意 .

    作为替代方案,您可以使用返回 Optional<Method> 对象的 TestInfo.getTestMethod() .
    如果在测试方法中使用检索方法,则甚至不需要测试 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());
    }
    

相关问题