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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 14:05:58