JUnit测试用例中的Thread.sleep

时间:2015-02-24 12:09:04

标签: java junit

我正在编写测试用例以测试对象行为。

当对象被实例化时,它必须允许调用方法,只有在500毫秒内调用它时才调用call(),否则它必须抛出异常。

我设计了这样的Junit测试用例:

@Test(expected = IllegalStateException.class)
public void testCallAfterTimeout() {
    MyObject o= new MyObject();
    //To ensure the timeout is occurred
    Thread.sleep(1000);
    o.call();
}

你认为这是一种好习惯还是我应该采用另一种方法?

非常感谢

2 个答案:

答案 0 :(得分:9)

在测试用例中使用(实际)时间有两个问题:

  1. 这绝不是确定性的。特别是当您正在寻找高精度时,测试用例将在95%的时间内成功。但有时它们会失败,这些是最难调试的类型。请注意,在多线程测试用例中使用Thread.sleep()时,这更加困难。
  2. 有睡眠的测试用例需要很长时间才能运行,过了一段时间这会让你的完整测试集运行起来很麻烦。
  3. 如果必须,你的方式是可以接受的。但还有其他选择:

    不要使用真正的时钟。而是使用可以从测试用例中控制的假(模拟/存根)时钟:

    @Test(expected = IllegalStateException.class)
    public void testCallAfterTimeout() {
        MyObject o= new MyObject();
        // Example function that you could make
        advanceClock(1000, TimeUnit.MILLISECONDS)
        o.call();
    }
    

    在你的对象中你必须注入一个时钟。 MyObject可能如下所示:

    class MyObject
    {
    
         public MyObject()
         {
               this(new Clock());
         }
    
         // Protected so only the test case can access it
         protected MyObject(Clock clock)
         {
               // Save clock as local variable, store creation time etc.
         }
    } 
    

    在Java 8中提供了一种机制,例如参见LocalDate.now()。但你也可以轻松实现自己的安静。

答案 1 :(得分:1)

无论有什么意义,你都在寻找麻烦......

如果您决定在60分钟后需要超时,您会等待一小时的测试吗? 超时应该是MyObject的参数,因此您可以将其设置为一些较小的值进行测试(甚至0以在测试时强制alwyas超时)。

其次,如果你想真正拥有可测试的时间相关函数,那么时间和超时应该与你的主逻辑(MyObject类)分开处理。例如,你可以使用mehtod canCallMethod()的Timekeeper类,它由你的MyObject类调用(并且在它的构造时设置)。以这种方式在测试中,使用自己的Timekeeper实现初始化MyObject类,该实现返回true或false,并验证MyObject类的行为是否符合预期。 MyObject可以使用默认构造函数,它始终使用“真实”时间戳,因此外部世界不会强制处理Timekeeper内部。