猿问

Java、TDD 不等于时为真?

由于某种原因,在课堂上测试此方法时,我们发现了一个我们无法理解的问题。当出于某种原因写作时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();


@Test

public 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());

}


有只小跳蛙
浏览 127回答 2
2回答

江户川乱折腾

终于有时间更深入地研究这个问题了。我开始实验,过了一段时间后发现,实际上并不是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++) {&nbsp; &nbsp; instants[i] = LocalDateTime.now(clock);&nbsp; &nbsp; for (int j = 0; j < 10000; j++) {&nbsp; &nbsp; &nbsp; &nbsp; k = j % 2;&nbsp; &nbsp; }}将值写入文件,然后将纳米差异与第一个值映射到图表中:正如您所看到的,该图(包含 1000 个值)会出现间歇性跳跃。这显然部分是由于底层系统的精度限制造成的。但令我震惊的是,前两个值始终不同。就好像在定期访问时操作系统开始缓存该值一段时间(可能是为了避免系统资源紧张)。但结果似乎是您设置自己在第三次和第四次调用时获得相同的值(除非已经过去了足够的时间)。这可以解释为什么您的测试在没有这些先前实例的情况下通过,而失败。顺便说一句,对于单元测试,您不想依赖系统时钟。确保您的业务代码从注入的 Clock 实例获取时间。然后,您可以注入自定义时钟进行测试,并测试您的代码是否会在 DST 转换日期或闰日运行,而无需等待几个月。

aluckdog

该测试涉及竞争条件并通过(有时),因为时间和添加语句会改变时间并因此改变测试结果。断言中检查的条件基本上是连续两个日期时间的纳秒部分是否相等。鉴于默认情况下由System.currentTimeMillis()内部使用并且它最多具有毫秒精度,如果获取纳秒数的第二个调用序列足够快(即导致在同一时间内完成调用的LocalDateTime.now实际调用序列),则检查将成功。System.currentTimeMillis毫秒作为第一个)。当您在实际断言之前调用用于获取纳秒值的函数时,会加载相应的类,相应方法的代码会进入 CPU 缓存等。这使得第二对获取纳秒数的调用运行得更快。
随时随地看视频慕课网APP

相关分类

Java
我要回答