You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

FreezeTime无法与FastAPI TestClient配合使用,测试失败求助

问题:freezegun无法与FastAPI TestClient配合冻结时间?

我发现FreezeTime似乎无法与FastAPI TestClient配合使用,编写了如下简单示例但测试失败,FreezeTime并未在此场景下覆盖时间值:

import datetime

from fastapi import FastAPI
from fastapi.testclient import TestClient
from pydantic import BaseModel
from freezegun import freeze_time

app = FastAPI()


class Message(BaseModel):
    message: str = "Hello World"
    timestamp: datetime.datetime = datetime.datetime.utcnow()


@app.get("/", response_model=Message)
def main() -> Message:
    return Message()


client = TestClient(app)

@freeze_time('2022-09-18T13:36:41.624237')
def test_read_main():
    response = client.get("/")
    assert response.status_code == 200
    assert response.json() == {
            'message': 'Hello World',
            'timestamp': '2022-09-18T13:36:41.624237'
            }

运行pytest后得到如下错误信息:

@freeze_time('2022-09-18T13:36:41.624237')
    def test_read_main():
        response = client.get("/")
        assert response.status_code == 200
>       assert response.json() == {
                'message': 'Hello World',
                'timestamp': '2022-09-18T13:36:41.624237'
                }
E       AssertionError: assert {'message': '...44:25.021208'} == {'message': '...36:41.624237'}
E         Omitting 1 identical items, use -vv to show
E         Differing items:
E         {'timestamp': '2022-09-18T13:44:25.021208'} != {'timestamp': '2022-09-18T13:36:41.624237'}
E         Use -v to get more diff

请问此类测试是否可通过FastAPI TestClient实现?


解决方案

问题出在Pydantic模型的默认值定义方式上:你直接把datetime.datetime.utcnow()作为默认值,这会在类定义时就执行一次,生成一个固定的时间戳,而不是每次创建Message实例时动态获取当前时间。所以即使测试时用freeze_time冻结了时间,返回的还是类加载时生成的旧时间。

解决方法是使用Pydantic的default_factory,它会在每次实例化模型时调用指定的函数来生成默认值,这样utcnow()就会在请求处理时执行,此时freeze_time的冻结已经生效。

修改后的完整代码:

import datetime

from fastapi import FastAPI
from fastapi.testclient import TestClient
from pydantic import BaseModel, Field  # 导入Field
from freezegun import freeze_time

app = FastAPI()


class Message(BaseModel):
    message: str = "Hello World"
    # 用default_factory替换直接赋值的默认值
    timestamp: datetime.datetime = Field(default_factory=datetime.datetime.utcnow)


@app.get("/", response_model=Message)
def main() -> Message:
    return Message()


client = TestClient(app)

@freeze_time('2022-09-18T13:36:41.624237')
def test_read_main():
    response = client.get("/")
    assert response.status_code == 200
    assert response.json() == {
            'message': 'Hello World',
            'timestamp': '2022-09-18T13:36:41.624237'
            }

修改后重新运行pytest,测试即可通过——每次请求生成Message实例时,都会调用被冻结的utcnow(),返回指定的时间戳。

内容的提问来源于stack exchange,提问作者dan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 20:35:23