记录运行 JUnit 测试所需的时间

Posted

技术标签:

【中文标题】记录运行 JUnit 测试所需的时间【英文标题】:Record time it takes JUnit tests to run 【发布时间】:2013-07-09 15:54:45 【问题描述】:

我想记录我的 JUnit 测试以编程方式运行需要多长时间。我在各种测试类中有大量测试,我想知道每个单独的测试方法需要多长时间才能运行。

我可以更改继承结构或对方法进行不同的注释,但我希望避免在测试方法本身以及用于设置测试业务逻辑的 before/after 方法中添加代码。

【问题讨论】:

您是否打算在构建工具(例如 Maven)中运行它?还是你想要 Eclipse 或类似的? 【参考方案1】:

您可以使用 JUnit StopWatch 规则并覆盖其方法,如 JUnit API 文档中提供的那样,只需在每个单独的测试用例类中包含一行代码,就可以将每个测试的时间打印到控制台或日志文件中。

    创建您的客户秒表类(提供示例)

    import java.util.concurrent.TimeUnit;
    import org.junit.AssumptionViolatedException;
    import org.junit.rules.Stopwatch;
    import org.junit.runner.Description;
    
    public class MyJUnitStopWatch extends Stopwatch
    
    private static void logInfo(Description description, String status, long nanos) 
        String testName = description.getMethodName();
        System.out.println(String.format("Test %s %s, spent %d microseconds",
                                  testName, status, TimeUnit.NANOSECONDS.toMicros(nanos)));
    
    
     @Override
       protected void succeeded(long nanos, Description description) 
           logInfo(description, "succeeded", nanos);
       
    
       @Override
       protected void failed(long nanos, Throwable e, Description description) 
           logInfo(description, "failed", nanos);
       
    
       @Override
       protected void skipped(long nanos, AssumptionViolatedException e, Description description) 
           logInfo(description, "skipped", nanos);
       
    
       @Override
       protected void finished(long nanos, Description description) 
           logInfo(description, "finished", nanos);
           
    
    

    使用该行创建一个 ParentTestClass,并且您的每个测试类都继承父测试类:

    public class ParentTestCase 
    
    
    @Rule
    public MyJUnitStopWatch stopwatch = new MyJUnitStopWatch();
    
    
    

子类继承父类。 Child 类或 before 或 after 方法没有其他变化。

public class TestUniqueCharacterString extends ParentTestCase     

    private String uniqueChars = null;

    @Before
    public void before()
        uniqueChars = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnop";
    

    @Test
    public void testMyUniqueCharacterMethod()


        UniqueCharacteString.doesStringHaveUniqueCharacters(uniqueChars);
    

或者

    在您的每个测试类中包含这一行

    @Rule
    public MyJUnitStopWatch stopwatch = new MyJUnitStopWatch();
    

    示例测试类:

    import org.junit.After;
    import org.junit.Before;
    import org.junit.Rule;
    import org.junit.Test;
    
    
    
    public class TestUniqueCharacterString     
    
    @Rule
    public MyJUnitStopWatch stopwatch = new MyJUnitStopWatch();
    
    private String uniqueChars = null;
    
    @Before
    public void before()
        uniqueChars = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnop";
    
    
    @Test
    public void testMyUniqueCharacterMethod()
    
    
        UniqueCharacteString.doesStringHaveUniqueCharacters(uniqueChars);
    
    
    @Test
    public void testGoodIsUniqueMethod()
            UniqueCharacteString.isUniqueCharacs(uniqueChars);
    
    
    @Test
    public void testGoodIsUniqueMethodWithoutArray()
        UniqueCharacteString.isUniqueCharsWithoutArray(uniqueChars);
    
    
    @After
    public void after()
        uniqueChars = "";
        
     
    

JUnit API 参考:

http://junit.org/apidocs/org/junit/rules/Stopwatch.html

样本输出

Test testMyUniqueCharacterMethod succeeded, spent 3250 microseconds
Test testMyUniqueCharacterMethod finished, spent 3250 microseconds
Test testGoodIsUniqueMethod succeeded, spent 70 microseconds
Test testGoodIsUniqueMethod finished, spent 70 microseconds
Test testGoodIsUniqueMethodWithoutArray succeeded, spent 54 microseconds
Test testGoodIsUniqueMethodWithoutArray finished, spent 54 microseconds

它还会显示失败和跳过的测试用例的时间。

【讨论】:

【参考方案2】:

尝试使用@Before 和@After。 使用@Before 或@After 注解的方法在测试之前或之后运行。

    @Before
    public void start() 
        start = System.currentTimeMillis();
    

    @After
    public void end() 
        System.out.println(System.currentTimeMillis() - start);
    

【讨论】:

谢谢,但是,我想避免将这些添加到我项目中的每个测试类中。有没有办法在一个地方添加这些? @oneself 使用这两种方法制作了一个父测试用例,并且对于您拥有的每个测试类,扩展父测试用例。 如果测试有自己的 before/after 方法,如何保证计时方法在测试前后立即运行?否则,我可能还会测量测试的 before/after 方法运行所花费的时间。 @oneself 如果是这样,您应该构建自己的计时功能。您必须自己触发和停止计时功能。【参考方案3】:

您还可以创建 @Rule 并实例化 TestWatcher 类。这对我有用。这是在扩展 TestCase 的类中定义的。

public class CompositeWithTeardownDBUnitTest extends DBTestCase 

DBTestCase 扩展了 TestCase

CompositeWithTeardownDBUnitTest 中的代码 sn-p

@Rule
public TestRule watcher = new TestWatcher() 

    protected void starting(Description description) 
        timeStart = System.currentTimeMillis();
        cal = Calendar.getInstance();
        System.out
                .println("===========================================================================");
        System.out.println("Test: " + description.getMethodName());
        System.out.println("Start Time: " + dateFormat.format(cal.getTime()));
        System.out
                .println("===========================================================================");
    

    protected void finished(Description description) 
        timeEnd = System.currentTimeMillis();
        double seconds = (timeEnd-timeStart)/1000.0;
        System.out
        .println("\n===========================================================================");
        System.out
        .println("Test completed - ran in: "+new DecimalFormat("0.000").format(seconds)+" sec");
        System.out
        .println("===========================================================================\n");

    
;

JUnit 测试类只是扩展了 CompositeWithTeardownDBUnitTest 这个类。

【讨论】:

【参考方案4】:

创建您自己的TestWatcher 实现,它可以捕获每个运行的测试方法。使用 Guava Stopwatch 您可以测量每个测试的时间:

public class TimeTestWatcher extends TestWatcher 
    private Stopwatch stopwatch = Stopwatch.createUnstarted();

    protected void starting(Description description) 
        stopwatch.start();
    

    protected void finished(Description description) 
        stopwatch.stop();

        String testName = description.getMethodName();
        long elapsed = stopwatch.elapsed(TimeUnit.MILLISECONDS);
        System.out.println(String.format("Test %s took %d ms.", testName, elapsed));
    
;

然后为每个测试类添加带有 TimeTestWatcher 的 JUnit @Rule 注释:

public class YourTest 

    @Rule
    public TimeTestWatcher watcher = new TimeTestWatcher();

    @Test
    public void testXXX() 

    @Test
    public void testYYY() 

【讨论】:

【参考方案5】:

除了现有答案之外,您还可以使用测试名称规则以及 BeforeAfter 方法在日志上显示方法名称。像这样:

public class ImageSavingTest 
    @Rule
    public TestName name = new TestName();

    private long start;

    @Before
    public void start() 
        start = System.currentTimeMillis();
    

    @After
    public void end() 
        System.out.println("Test " + name.getMethodName() + " took " + (System.currentTimeMillis() - start) + " ms");
    

    @Test
    public void foobar() 
        // test code here
    

将输出:

测试 foobar 耗时 1828 毫秒

【讨论】:

【参考方案6】:

您可以创建一个JUnit Rule 来记录通话前后的时间。此规则可用作实例和/或类规则,以便为每个单独的测试方法以及每个测试类提供时间。

【讨论】:

【参考方案7】:

如果您使用@Before 和@After 注释并注意junit 测试用例的开始和结束时间。然后找到两个时间戳的差异应该会给你测试用例的执行时间。像这样的:

 public class Example 

    long startTime;
    long endTime;

    @Before public void recordStartTime() 
        startTime = System.currentTimeMillis();
    
    @Test public void testSomething() 
          //test method
    
    @After public void recordEndAndExecutionTime() 
        endTime = System.currentTimeMillis();
        System.out.println("Last testcase exection time in millisecond : " + (endTime - startTime));
    
 

【讨论】:

以上是关于记录运行 JUnit 测试所需的时间的主要内容,如果未能解决你的问题,请参考以下文章

markdown 使用控制台日志记录运行函数所需的时间

如何使 JUnit 测试用例按顺序运行?

有没有办法以所需的顺序运行Specflow场景(C#Selenium)?

如何让 /bin/bash 在 Composer 中运行所需的 php 版本? [复制]

Websocket 的单元测试返回 Null

缺少所需的架构 arm64