Java 中的时间旅行——你有时钟吗?

Java 中的时间旅行——你有时钟吗?

Photo by 史蒂夫·约翰逊 on 不飞溅

时间旅行 !我敢打赌,我们中的许多人都想体验它,但你猜怎么着——你可以用 Java 来做!你只需要一个

在这篇文章中,我将向您展示如何:

  • 管理您的应用程序假定的时间 现在
  • 测试使用静态方法的代码 java.time.* 包裹

什么是 ” 现在” ?

日期和时间似乎只是一个简单的概念。一旦你开始使用不同的时区,你会发现它可能会很棘手。下面我只列出了几个技术难题:

  • 你住在时区 一个 ,并且您的生产基础架构在时区中运行 . LocalDateTime.now() 在本地机器和生产环境中返回不同的结果
  • 您的数据库服务器运行在与您不同的时区 - 您的 SQL 是在本地准备和测试的,但它可能会在实际服务器上应用不同的偏移量
  • 您的服务和数据库在不同的时区运行 - 保存日期和时间时,哪一方应该应用偏移量?你的服务还是你的数据库?如果它们都没有(或两者)会应用偏移偏移怎么办?
  • 您将所有内容都存储在 UTC 中 - 太棒了!但是,如果想知道客户执行某项操作的时间是什么时候呢?

正如你所看到的,它在复杂系统中都不是那么微不足道,在本地也不是!让我们看一个常见的用例。

现在 + 一些东西

业务需求通常基于时间 - 例如,如果您购买某物,您可以在 30 天后付款。您的系统必须以某种方式计算此截止日期 - 通常您会看到以下解决方案:

问题是那个方法 LocalDateTime.now() 是静态的,要模拟它并不容易。那怎么测试呢?它已经变得很棘手,但它仍然是可能的。

如果您不想模拟静态方法,并且精确到几秒钟对您来说“足够好”,您可以提出以下想法:

这些是测试产生的日志——如您所见,以毫秒为单位存在差异。在这种情况下,秒匹配,但如果您再次运行测试,它们可能不会匹配。

 实际截止日期 2022-10-03T23:46:50。 **307535**  
 预计截止日期 2022-10-03T23:46:50。 **306746**

问题

  • 针对静态方法结果的测试和断言——正如您所见,它更复杂,因为您不能 100% 确定静态方法产生的结果是什么
  • 与时间相关的需求问题 - 有时业务需求非常复杂,并且在很大程度上取决于时间,例如价格可能是动态的,工作日晚上的东西更便宜,但仅限于冬季等。 在这种情况下,您想要尽可能精确地比较日期和时间

想法

以下是这两个问题的一些解决方案和“解决方案”。首先让我们考虑一下这些可能有效,但在我看来不是很好的方法:

工作,但不是很好

  • 将“now”作为参数传递给方法 - 哎呀,这很痛苦。是的,它会让测试变得更容易,但是……代码复杂性增加了,因为你的方法接受了一个额外的参数。为什么该方法的客户需要向您提供此日期和时间?如果某些客户将其用作优势,例如将 2100 年作为“现在”传递(如前面示例中的截止日期)怎么办?
  • 模拟静态方法 - 你可以使用一些测试库或 AOP 来做到这一点,但是……在我看来,如果你必须模拟一个静态方法来测试你的业务需求,那么你做错了什么 静态非常适合 效用 方法,但是静态方法的每次使用都是隐藏的依赖项

更好,但不是最好的

  • 风俗 时间服务 包装静态方法。您可以将此服务注入其他服务,并在测试中模拟您的服务

优点:

  • 可见依赖——你知道时间依赖
  • 易于测试和模拟 - 在您的测试中,您可以简单地模拟 时间服务 行为

缺点:

  • 在整个应用程序中(可能)使用的附加类
  • 这个类可能会快速增长 - 也许这没什么大不了的,但是如果您使用大量静态方法,则许多包装方法可能会迅速增加 java.time 包裹。想象一下这个类有 20 个公共方法——对我来说,这听起来不错,很老 效用 上课,但是用漂亮的丝带绑起来也不是太 小号
  • 这是其他类的新依赖项

(个人)最好的

  • 使用 java.time.时钟 作为时间提供者的类 - 它使测试更容易,它使时间旅行成为可能

正如您在 javadoc 中所读到的:

时钟的使用是可选的。所有关键的日期时间类也有一个 now() 工厂方法,它使用默认时区的系统时钟。这种抽象的主要目的是允许在需要时插入备用时钟。应用程序使用对象而不是静态方法来获取当前时间。这可以简化测试。

应用程序的最佳实践是将时钟传递给任何需要当前时刻和时区的方法

听起来是一个很好的解决方案!

优点:

  • 类来自 JDK - 不需要第 3 方库
  • 使时间旅行成为可能 - 您可以模拟应用程序在任何时间点运行

缺点:

  • 这是其他类的新依赖项(但您无法克服它)

时钟bean配置

下面的示例显示了如何配置 bean 使用 Spring 的配置。

本地日期和时间

使用此配置将返回本地日期和时间结果

固定时钟

使用此配置将返回固定结果 - now() 方法调用将始终返回相同的时间

世界标准时间

时间旅行

如果您想及时旅行,并配置什么是 现在 为您服务,您可以通过以下方式实现这一目标:

此配置寄存器 应用程序上下文中的 bean。如果您不设置任何 时间旅行。** 属性,您的应用将使用系统的默认时区。如果你设置 时间旅行。** 属性,例如以下列方式

 time-travel.enabled=true  
 时间旅行.instant=2022-09-01T12:00:00.00Z  
 time-travel.zone=UTC

然后 现在 在您的应用程序中将始终为 2022–09–01T12:00:00。

概括

在这个故事中,我描述了:

  • 您可能遇到的日期、时间和时区有什么问题
  • 为什么那些伟大的静态方法来自 java.time 包不是最简单的测试
  • 您如何处理申请中的时间和时间旅行
  • 在 Java 中处理时间的最佳(我认为)方法是什么

资源和进一步阅读:

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明

本文链接:https://www.qanswer.top/15560/57450509

posted @   哈哈哈来了啊啊啊  阅读(32)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示