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

13
我想要以程序记录JUnit测试运行的时间。我的测试类中有很多测试方法,我希望能够找出每个测试方法的执行时间。
我可以更改继承结构或在方法上添加注释,但是我不想在测试方法内部或设置测试业务逻辑的before/after方法中添加代码。

你打算在构建工具中运行它吗,例如Maven?还是想要Eclipse或类似的工具? - Matthew Farwell
7个回答

13

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

  1. 创建您的自定义StopWatch类(已提供示例)

    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);
       }    
    }
    
  2. 创建一个名为ParentTestClass的父测试类,并让你的每个测试类都继承自该父测试类:

  3. public class ParentTestCase {
    
    
    @Rule
    public MyJUnitStopWatch stopwatch = new MyJUnitStopWatch();
    
    }
    

    子类继承父类。在子类或前后方法中没有其他更改。

public class TestUniqueCharacterString extends ParentTestCase {    

    private String uniqueChars = null;

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

    @Test
    public void testMyUniqueCharacterMethod(){


        UniqueCharacteString.doesStringHaveUniqueCharacters(uniqueChars);
    }

或者

  1. 在每个测试类中都要包含这一行

@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

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


11

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

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

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

谢谢,不过我想避免在我的项目中的每个测试类中都添加这些。有没有一种方法可以在一个地方添加它们? - oneself
@oneself 使用这两个方法创建了一个父级测试用例,并且对于每个测试类,都要扩展父级测试用例。 - Heejin
如果测试有自己的before/after方法,我怎么能保证计时方法会在测试之前和之后立即运行呢?否则,我可能还会测量测试的before/after方法运行所需的时间。 - oneself
如果是这样,你应该建立自己的定时函数。你必须自己触发和停止定时函数。 - Heejin

2
您可以创建一个@Rule并实例化TestWatcher类。这是对我有用的。这是在扩展TestCase类的类中定义的。
public class CompositeWithTeardownDBUnitTest extends DBTestCase {

DBTestCase扩展了TestCase

在CompositeWithTeardownDBUnitTest中的代码片段

@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的类。


2

创建自己的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));
    }
};

然后在每个测试类中使用JUnit的@Rule注解和你的TimeTestWatcher

public class YourTest {

    @Rule
    public TimeTestWatcher watcher = new TimeTestWatcher();

    @Test
    public void testXXX() {}

    @Test
    public void testYYY() {}
}

1
如果你使用了@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));
    }
 }

1
除了现有的答案,您可以使用一个规则来测试名称,以及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 毫秒


0
你可以创建一个 JUnit Rule,它可以记录 before/after 调用之间的时间。这个规则可以作为实例和/或类规则使用,以获取每个单独的测试方法和每个测试类的时间。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接