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

FastAPI/SQLModel集成测试:不使用freezegun的时间冻结方案,及freezegun测试登录日志时间不一致问题求助

FastAPI/SQLModel集成测试:不使用freezegun的时间冻结方案,及freezegun测试登录日志时间不一致问题求助

大家好,我最近在用pytest给FastAPI+SQLModel的应用写集成测试,想在应用内部直接冻结时间。freezegun在单元测试场景下用着没问题,但在运行中的FastAPI应用里就不太靠谱了,遇到了一个奇怪的时间不一致问题,想请教下大家怎么解决,或者有没有不用freezegun的替代方案?

问题场景与测试代码

我用了pytest-freezegun插件,测试代码如下:

@pytest.mark.asyncio
async def test_login__authorize__check_log_date(session, freezer):
    # Arrange
    await push_one_user()
    payload = {USERNAME_KEY: USER_LOGIN, PASSWORD_KEY: PLAIN_PASSWORD}
    
    # Act
    await execute_post_request("/auth/login", payload=payload)
    
    # Assert
    last_log = (await get_user_log(session)).pop()
    assert last_log.date_connexion == datetime.now()

测试结果

运行后断言失败,两个FakeDatetime时间对不上:

Expected :FakeDatetime(2025, 7, 29, 7, 19, 16, 33142)
Actual   :FakeDatetime(2025, 7, 29, 9, 19, 16, 133177)

奇怪的是,我在代码里用的是不带时区的datetime.now(),按道理应该被freezegun统一替换才对,但结果却差了两个小时左右?

相关服务端代码

AuthService中的认证逻辑

class AuthService:
    @classmethod
    async def authenticate_user(
        cls,
        session: SessionDep,
        username: str,
        password: str,
    ) -> Optional[User]:
        user = await UserService.get_user_by_login_with_validity_check(
            session, username
        )
        if not user:
            raise CREDENTIALS_EXCEPTION
        is_correct_password = await PasswordService.verify_password(
            password, user.hashed_password
        )
        if is_correct_password is not True:
            raise CREDENTIALS_EXCEPTION
            
        user.set_last_login_date()
        # 怀疑问题出在这个LoginLog实例化环节
        login_log = LoginLog(user=user)
        session.add(login_log)
        await session.commit()
        return user

LoginLog模型定义

class LoginLog(SQLModel, table=True):
    __tablename__ = "login_log"
    
    id: Optional[int] = Field(default=None, primary_key=True)
    # 重点怀疑这个default_factory的执行时机
    date_connexion: datetime = Field(default_factory=datetime.now)
    id_user: uuid.UUID = Field(foreign_key="user.id")
    
    # Relations
    user: "User" = Relationship(back_populates="connexions")

我现在的疑惑是:明明测试和应用代码里都用了datetime.now(),freezegun应该把所有datetime.now()的调用都替换成冻结的时间才对,但为什么LoginLog里生成的时间和测试里取的datetime.now()会不一样?是不是SQLModel的default_factory在session提交的时候才会执行?或者是异步场景下freezegun的事件循环兼容问题?

另外,如果freezegun在这种集成测试场景下不好用,有没有其他方案可以在FastAPI应用中可靠地冻结内部时间?

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.07 10:09:29