最好的时机就是现在:一个Bug的故事
内容提要
作者为数据模型添加5年TTL后测试失败,报错称执行时间在10年后。原因是测试硬编码当前时间为2018年,导致2028年目标时间与2018年相差超10年。作者未改硬编码日期,而是改为基于真实当前时间减90天,避免埋下定时炸弹。
延伸解读
硬编码日期的隐患
测试中将当前时间硬编码为2018年1月1日,导致后续添加5年TTL时,目标时间2028年与模拟当前时间相差超过10年,触发框架限制。这种硬编码看似方便,却会随着真实时间推移而逐渐偏离,最终在某个时间点突然引发失败,且原因不易察觉。
修复策略:模拟相对时间
作者没有简单地将硬编码日期改为2023年,而是改为基于真实当前时间减去90天。这样模拟的“现在”始终与真实时间保持相对关系,避免了未来某一天再次因时间差过大而失败。这种修复方式更贴近真实时间流动的特性,也减少了后续维护成本。
偶然发现背后的幸运
这个bug的暴露依赖于多个条件同时成立:TTL设置为5年、测试硬编码日期为2018年、框架限制为10年。如果TTL更短或硬编码日期更晚,问题可能不会立即显现,而是潜伏更久,导致未来排查困难。因此,及时暴露并修复此类问题,实际上是一种幸运。
Q&A
为什么给数据模型添加5年TTL后测试会失败?
因为测试中硬编码了当前时间为2018年1月1日,而数据创建时间是2023年5月4日,加上5年TTL后目标时间为2028年5月3日,与模拟的当前时间相差超过10年,触发了TTL框架的“执行时间超过10年”异常。
TTL框架在什么情况下会抛出异常?
当TTL的过期执行时间(即创建时间加上TTL偏移量)与当前时间的差值超过3650天(约10年)时,框架会抛出DeltaTooBigException异常。
作者最终如何修复这个失败的测试?
作者没有硬编码新的固定日期,而是将模拟的当前时间改为基于真实当前时间减去90天,即mock_now = datetime.datetime.now() - datetime.timedelta(days=90),这样模拟时间会随真实时间动态变化。
为什么不应该在单元测试中硬编码模拟的当前时间?
因为真实世界中“现在”是不断变化的,硬编码固定时间会让测试永远停留在过去,可能导致未来某天测试意外失败,且难以排查。作者认为模拟函数应尽可能接近其真实行为。
如果作者只是把硬编码日期改成2023年1月1日,会有什么后果?
那会埋下定时炸弹,测试会在2028年1月1日再次失败,因为到那时模拟的当前时间与目标时间的差值又会超过10年。
这个Bug在什么条件下可能不会被发现?
如果TTL设置更短(如3年)、前一位工程师在更晚时间写测试(如2019年)、或TTL框架更宽松(如允许12年),那么这个测试可能不会在当前被触发,但未来仍可能因其他原因失败。