• 2014-06-12

    测试“时间”

    版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
    http://www.blogbus.com/dreamhead-logs/271669052.html

    在单元测试中,与时间相关的测试总是让人很头疼。举个例子,我们希望做一个定期过期缓存,比如30分钟过期,这该怎么测试呢?等30分钟?那要是过期时间是3天,你打算把开发时间全部交给等待,然后向老板汇报,我在等测试。祝你有一个糊涂的老板!

    我们不妨分析一下,看看有没有什么不那么令人发指的解决方案。以上面的缓存为例,缓存怎么知道过了多长时间,它肯定是在哪取个时间?对于Java应用来说,很有可能最终就是调到System.currentTimeMillis()或者nanoTime()。但是,这两个方法都是静态方法,想用Mock的人可以休息了。

    那么直接调用操作系统的方式修改时间呢?可以,只是一旦与具体的OS相关,各种跨平台的问题就随之而来,总而言之,麻烦。

    许多计算机问题都是可以通过引入间接层来搞定的,这一次又到了“间接层”的表演时间了。正如前面所说,与时间相关的应用最终可能都要到最底层那两个方法。如果我们可以做一层隔离,让所有的调用都来到间接层,我们就可以在间接层上做手脚了。

    事实上,这是一个如此通用的问题,以致于我们甚至都不需要提供自己的解决方案。

    在Guava中,有一个类叫Ticker,它提供了一个方法叫做read(),他就是我们的间接层。缺省情况下,我们会直接使用Ticker.systemTicker(),顾名思义,它是系统提供的ticker,read方法最终会调用倒System.nanoTime()。如果测试需要,我们可以自己Mock出来一个Ticker,就像下面这样:

    Ticker ticker = mock(Ticker.class);
    long time = Ticker.systemTicker().read();
    when(ticker.read()).thenReturn(time, time + TimeUnit.SECONDS.toNanos(10));

    这段代码第一次调用时返回一个时间,第二次调用返回的时间就是第一次调用的10秒之后了。所以,如果你的代码依赖于Ticker,剩下的魔法你都可以自己完成了。

    不过,这还不是终点。

    在Java中,谈及时间和日期,Joda Time已经是一个必选项了。实际上,Joda Time也为自己的用户提供了一套解决时间测试的方案,它有一个DateTimeUtils.setCurrentMillisFixed方法,我们可以传入一个固定的时间,比如,10秒后。时间就固定在那了。当然,如果你需要让时间重新流动起来,需要调用一下DateTimeUtils.setCurrentMillisSystem。

    原理很简单,Joda Time底层最终会调用DateTimeUtils.currentTimeMillis方法,而我们调用的set方法就会让这个方法返回不同的值,当然,缺省的是系统时间。

    Ticker和DateTimeUtils.currentTimeMillis在单位上有个差别,Ticker用的是System.nanoTime(),而DateTimeUtils.currentTimeMillis则如名所示,使用的是System.currentTimeMillis()。

    好了,这就是测试“时间”的方法。

    分享到:

    历史上的今天:

    选择第三方 2005-06-12
    引用地址:

    评论

  • wao. 你的粉丝已经两千多人了啊!