如何实现时间穿越?PowerMock系列之2

2020-12-01 09:29:55 浏览数 (1)

测试场景

如果一个场景需要在9012年的愚人节才能被触发?如何进行单元测试?

在笔者测试的某些系统中,存在一些与时间相关的系统功能。如某个程序会在每天的指定时间,如下午6点被触发,完成与外部公司的数据交换。 在系统测试时,往往需要通过修改linux的系统时间等方式来触发上述功能进而完成测试过程。这时非常不方便的,而且有时候还会因为修改了操作系统时间忘记改回,导致其它应用产生问题,如连接超时等。当然也有在隔离网络内提供NTP时钟服务的方案,只是需要网络层面的支持,测试成本较高,一般用于系统测试中。 而在单元测试时,为了不受外部约束,保证测试用例的健壮性,需要对系统时间进行mock。如以下的一个被测方法

代码语言:javascript复制
public long getDoubleTime(){
        return new Date().getTime();
    }

将返回当前的系统时间。实际程序中,再将获得时间与预设定的时间进行比较,即可完成按时间触发某项工作的功能。

Mock实现

我们希望能mock掉Date类,让其能返回任意给定的预设时间,从而简化UT。但对于述代码进行分析后我们发现,UT所面临的问题是,由于时间的获取是在一个 new Date()这样的内部匿名对象中完成,无法采用mockito等普通mock工具实现mock。 因此,需要使用Powermock来完成这一需求。测试样例如下:

代码语言:javascript复制
@Test
public void testMockTime() throws Exception {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    sdf.setTimeZone(TimeZone.getTimeZone("GMT 8:00"));
    Date NOW = sdf.parse("2018-06-16 00:00:01");
    // everytime we call new Date() inside a method of any class
    // declared in @PrepareForTest we will get the NOW instance
    PowerMockito.whenNew(Date.class).withNoArguments().thenReturn(NOW);
//Test
SystemClass systemClass = new SystemClass();
//long time=systemClass.getDoubleTime();
assertEquals(systemClass.getDoubleTime(),NOW.getTime());
}

上述代码的核心是以下的一行代码,

代码语言:javascript复制
 PowerMockito.whenNew(Date.class).withNoArguments().thenReturn(NOW);

每当SUT中调用了无参的Date()构造方法来new 出Date类的实例时,将返回一个预设的NOW时间。这样,我们就可以去触发那些只在指定时间才能触发的功能了。

0 人点赞