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
相关产品推荐
相关产品推荐

