为了账号安全,请及时绑定邮箱和手机立即绑定

Java、TDD 不等于时为真?

Java、TDD 不等于时为真?

有只小跳蛙 2023-10-19 21:28:18
由于某种原因,在课堂上测试此方法时,我们发现了一个我们无法理解的问题。当出于某种原因写作时System.out.println();它就过去了?有人可以解释为什么会发生这种情况吗?public class Zones {public ZoneId getZoneId(String input) {    if (input.equalsIgnoreCase("Stockholm")) {        return ZoneId.of("Europe/Stockholm");    }    else if (input.equalsIgnoreCase("Shanghai")) {        return ZoneId.of("Asia/Shanghai");    } else if (input.equalsIgnoreCase("Toronto")) {        return ZoneId.of("America/Toronto");    }    else if (input.equalsIgnoreCase("Hamburg")) {        return ZoneId.of("Europe/Berlin");    }    else return null;}public LocalDateTime getZoneTime(ZoneId zoneId) {    LocalDateTime lt = LocalDateTime.now(zoneId);    return lt;}}private Zones z = new Zones();@Testpublic void getZoneTimeTest () {    System.out.println(z.getZoneTime(zIDToronto).getNano() );    System.out.println(LocalDateTime.now(zIDToronto).getNano() );    assertTrue(z.getZoneTime(zIDToronto).getNano() == LocalDateTime.now(zIDToronto).getNano());}
查看完整描述

2 回答

?
江户川乱折腾

TA贡献1851条经验 获得超5个赞

终于有时间更深入地研究这个问题了。


我开始实验,过了一段时间后发现,实际上并不是System.out.println它的存在影响了结果,而是你LocalDateTime在它之前实例化了2个实例。


深入挖掘LocalDateTime和SystemClock(它所委托的) 的代码,我发现亚毫级精度是通过调用本机调用 来实现的jdk.internal.misc.VM#getNanoTimeAdjustment。


最后一个调用是特定于操作系统的。我对它进行了一些实验,发现它不会线性返回值,因为它在循环中被调用(假设我的循环运行得相当规律)。


所以我决定运行一些代码来映射返回的纳米值。


我制作了这个示例代码:


Clock clock = Clock.systemDefaultZone();


int samples = 1_000;

LocalDateTime[] instants = new LocalDateTime[samples];


int k = 0;


for (int i = 0; i < samples; i++) {

    instants[i] = LocalDateTime.now(clock);

    for (int j = 0; j < 10000; j++) {

        k = j % 2;

    }

}

将值写入文件,然后将纳米差异与第一个值映射到图表中:

https://img1.sycdn.imooc.com/65312f3100016cf506440441.jpg

正如您所看到的,该图(包含 1000 个值)会出现间歇性跳跃。这显然部分是由于底层系统的精度限制造成的。但令我震惊的是,前两个值始终不同。就好像在定期访问时操作系统开始缓存该值一段时间(可能是为了避免系统资源紧张)。

但结果似乎是您设置自己在第三次和第四次调用时获得相同的值(除非已经过去了足够的时间)。

这可以解释为什么您的测试在没有这些先前实例的情况下通过,而失败。

顺便说一句,对于单元测试,您不想依赖系统时钟。确保您的业务代码从注入的 Clock 实例获取时间。然后,您可以注入自定义时钟进行测试,并测试您的代码是否会在 DST 转换日期或闰日运行,而无需等待几个月。


查看完整回答
反对 回复 2023-10-19
?
aluckdog

TA贡献1847条经验 获得超7个赞

该测试涉及竞争条件并通过(有时),因为时间和添加语句会改变时间并因此改变测试结果。

断言中检查的条件基本上是连续两个日期时间的纳秒部分是否相等。

鉴于默认情况下由System.currentTimeMillis()内部使用并且它最多具有毫秒精度,如果获取纳秒数的第二个调用序列足够快(即导致在同一时间内完成调用的LocalDateTime.now实际调用序列),则检查将成功。System.currentTimeMillis毫秒作为第一个)。

当您在实际断言之前调用用于获取纳秒值的函数时,会加载相应的类,相应方法的代码会进入 CPU 缓存等。这使得第二对获取纳秒数的调用运行得更快。


查看完整回答
反对 回复 2023-10-19
  • 2 回答
  • 0 关注
  • 126 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信