FreezeGun:让 Python 测试自由操控时间

写测试的时候,时间相关的逻辑总是特别头疼。比如一个函数会在每天零点执行清理操作,你总不能真的等到半夜再跑测试吧?或者某个功能只在特定日期生效,手动改系统时间又太麻烦了。

这就是 FreezeGun 要解决的问题。这个 Python 库已经有 4,514 个 Star,专门做一件事:在测试里 mock datetime 模块,把时间定在你想要的任意时刻。

正文顶部截图

三种用法,覆盖所有场景

FreezeGun 的使用方式很灵活,提供了装饰器、上下文管理器和原始调用三种形式。

最常用的是装饰器。给测试函数加上 @freeze_time("2012-01-14"),函数内部所有获取当前时间的调用都会返回 2012 年 1 月 14 日。也可以直接装饰在整个测试类上,类里的每个测试方法都会生效。

上下文管理器适合只需要在代码片段里冻结时间的场景。用 with freeze_time("2012-01-14"): 包起来,离开这个块之后时间自动恢复正常。这种方式的隔离性更好,不会影响其他测试。

如果你需要更精细的控制,可以用原始调用方式。先 freezer = freeze_time("2012-01-14"),然后手动调用 freezer.start()freezer.stop(),完全由你决定什么时候开始和结束。这种方式适合需要在测试中途动态冻结和解冻的情况。

不只是冻结,还能让时间动起来

FreezeGun 默认会把时间完全定住,但这有时候不够。比如你测的是一个定时轮询的逻辑,需要验证时间推进后的行为。

这时候可以用 tick=True 参数。时间会从你指定的起点开始,然后继续正常流逝。或者用 auto_tick_seconds=15,每次调用 now() 时间自动前进 15 秒,适合测试按固定间隔执行的任务。

还有一个实用的 move_to 方法,可以在冻结期间把时间跳到另一个指定时刻。比如先冻结在年初测试年初逻辑,然后 move_to 到年末继续测年末逻辑,全部在一个测试函数里完成,不用拆成多个测试。

时区处理也考虑到了。通过 tz_offset 参数可以指定时区偏移,datetime.utcnow()datetime.now() 会按照偏移量返回对应的时间。测试跨时区的功能时这个功能很省事。

安装和配置

安装很简单:

pip install freezegun

另外 FreezeGun 会自动忽略一些不需要 mock 的包,比如 threading、Queue、selenium 等。如果你使用其他库时也遇到类似问题,可以通过 ignore 参数临时指定要跳过的包,或者用 freezegun.configure 设置全局的忽略列表。

README区域截图

适合什么人用

如果你写 Python 测试,且代码里涉及时间判断,FreezeGun 基本是必装工具。常见的使用场景包括:测试 cron 任务或定时器逻辑、验证日期边界条件、模拟过期时间、测试只在特定时段生效的功能等。

比起手动 patch datetime 模块,FreezeGun 的 API 设计得更简洁,而且考虑了很多边界情况。比如它内部用 dateutil 解析时间字符串,支持 “Jan 14th, 2012” 这种自然语言写法;也支持传入 lambda 或生成器来动态决定冻结的时间点。

这个库已经存在多年,4,514 个 Star 说明社区认可度不错。API 成熟稳定,文档也比较完整。对于 Python 开发者来说,它是一个省时间的选择。

不错。API 成熟稳定,文档也比较完整。对于 Python 开发者来说,它是一个省时间的选择。

更多推荐