如何在FastAPI测试中处理datetime.now()这类外部依赖?
解决FastAPI测试中datetime.now()动态依赖导致的断言失败问题
问题背景
FastAPI接口依赖datetime.now()的动态返回值,导致测试用例每次运行的断言结果不一致,无法通过测试。尝试过猴子补丁、依赖注入、环境变量三种方案但均未成功,需要可行的解决办法。
复现代码
lib_datetime.py
from datetime import datetime from datetime import timezone def now() -> datetime: return datetime.now(timezone.utc)
fastapi_webserver.py
from fastapi import FastAPI from lib_datetime import now from datetime import datetime app = FastAPI() @app.get('/datetime_now') def datetime_now(): the_datetime_now = now() the_datetime_now_str = the_datetime_now.strftime('%Y-%m-%d %H:%M:%S.%f %z') return { 'datetime_now': the_datetime_now_str, }
test_fastapi_webserver.py(原测试代码)
from fastapi.testclient import TestClient from fastapi_webserver import app from datetime import datetime from datetime import timezone client = TestClient(app) def test_fastapi_webserver_datetime_now(): datetime_now = datetime(year=2024, month=7, day=20, tzinfo=timezone.utc) datetime_now_str = datetime_now.strftime('%Y-%m-%d %H:%M:%S.%f %z') response = client.get('/datetime_now') print(response.json()) assert response.status_code == 200 assert response.json() == { 'datetime_now': datetime_now_str, }
可行解决方案
方案1:正确实现猴子补丁
之前猴子补丁失败的核心原因是patch路径错误,需要patch被FastAPI模块实际导入的now引用,而非原模块中的函数。
修改后的测试代码:
from fastapi.testclient import TestClient from fastapi_webserver import app from datetime import datetime, timezone from unittest.mock import patch client = TestClient(app) def test_fastapi_webserver_datetime_now(): test_datetime = datetime(year=2024, month=7, day=20, tzinfo=timezone.utc) test_datetime_str = test_datetime.strftime('%Y-%m-%d %H:%M:%S.%f %z') # 关键:patch fastapi_webserver模块中的now函数,而非lib_datetime中的 with patch('fastapi_webserver.now', return_value=test_datetime): response = client.get('/datetime_now') assert response.status_code == 200 assert response.json() == {'datetime_now': test_datetime_str}
方案2:FastAPI依赖注入(推荐)
将时间获取逻辑封装为FastAPI依赖项,这是框架推荐的外部依赖管理方式,生产和测试环境可无缝切换实现。
步骤1:修改lib_datetime.py,添加依赖函数
from datetime import datetime, timezone def now() -> datetime: return datetime.now(timezone.utc) # 定义FastAPI依赖项 def get_now(): return now()
步骤2:修改FastAPI接口,使用依赖注入
from fastapi import FastAPI, Depends from lib_datetime import get_now from datetime import datetime app = FastAPI() @app.get('/datetime_now') def datetime_now(current_time: datetime = Depends(get_now)): current_time_str = current_time.strftime('%Y-%m-%d %H:%M:%S.%f %z') return { 'datetime_now': current_time_str, }
步骤3:编写测试代码,替换依赖项
from fastapi.testclient import TestClient from fastapi_webserver import app, get_now from datetime import datetime, timezone client = TestClient(app) def test_fastapi_webserver_datetime_now(): test_datetime = datetime(year=2024, month=7, day=20, tzinfo=timezone.utc) test_datetime_str = test_datetime.strftime('%Y-%m-%d %H:%M:%S.%f %z') # 定义模拟依赖函数 def mock_get_now(): return test_datetime # 覆盖原依赖 app.dependency_overrides[get_now] = mock_get_now try: response = client.get('/datetime_now') assert response.status_code == 200 assert response.json() == {'datetime_now': test_datetime_str} finally: # 测试后清除覆盖,避免影响其他测试用例 app.dependency_overrides.clear()
方案3:环境变量控制(不推荐)
通过环境变量切换时间获取逻辑,侵入性较强,适合特殊场景使用。
步骤1:修改lib_datetime.py
from datetime import datetime, timezone import os def now() -> datetime: # 测试环境读取固定时间 test_time_str = os.getenv('TEST_DATETIME') if test_time_str: return datetime.fromisoformat(test_time_str).replace(tzinfo=timezone.utc) # 生产环境返回当前时间 return datetime.now(timezone.utc)
步骤2:修改测试代码
from fastapi.testclient import TestClient from fastapi_webserver import app from datetime import datetime, timezone import os client = TestClient(app) def test_fastapi_webserver_datetime_now(): test_datetime = datetime(year=2024, month=7, day=20, tzinfo=timezone.utc) test_datetime_str = test_datetime.strftime('%Y-%m-%d %H:%M:%S.%f %z') # 设置测试环境变量 os.environ['TEST_DATETIME'] = test_datetime.isoformat() try: response = client.get('/datetime_now') assert response.status_code == 200 assert response.json() == {'datetime_now': test_datetime_str} finally: # 清除环境变量 del os.environ['TEST_DATETIME']
内容的提问来源于stack exchange,提问作者user2138149
相关产品推荐
相关产品推荐

