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

如何在Pytest中全局Mock已导入的SqlAlchemy Session变量?

全局Mock SqlAlchemy scoped_session的解决方案

为什么原有Mock代码不生效?

业务模块(如app/user/users.py)在导入阶段就会将utils.sessions.session的引用保存到自身的命名空间中。直接patchutils.sessions.session只会修改原模块的变量,无法更新已经导入到业务模块的旧引用,导致Mock不生效。此外,你的mocked_session函数返回的scoped_session("xxx")写法错误——scoped_session需要传入sessionmaker实例,而非字符串。


解决方案一:修改代码结构(推荐)

通过工厂函数提供session,而非直接导出变量,这样只需Mock工厂函数就能全局生效,无需修改业务模块的调用逻辑(可兼容旧代码)。

1. 重构utils/sessions.py

# utils/sessions.py
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy import create_engine

# 生产环境配置
def get_session():
    return scoped_session(sessionmaker(
        bind=create_engine('your_prod_db_url'),
        autocommit=False,
        autoflush=False
    ))

# 保持原有导出,兼容已有的业务代码
session = get_session()

2. 业务模块改用工厂函数(可选,也可继续用原导入,但推荐切换)

# app/user/users.py
from utils.sessions import get_session

class UserRepo:
    def get_user(self, id):
        # 调用工厂函数获取session
        return get_session().query(User).filter(User.id == id)

3. 全局Mock Fixture

# tests/conftest.py
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy import create_engine
import pytest
from unittest.mock import patch

def mocked_session():
    # 返回测试用的scoped_session实例(比如内存SQLite)
    return scoped_session(sessionmaker(
        bind=create_engine('sqlite:///:memory:'),
        autocommit=False,
        autoflush=False
    ))

@pytest.fixture(autouse=True)
def mock_session_factory():
    # Mock工厂函数,全局替换所有session来源
    with patch('utils.sessions.get_session', return_value=mocked_session()) as mock_get:
        yield mock_get

解决方案二:不修改业务代码,全局更新引用

如果不想改动现有代码,可以在Mock后重新加载所有使用session的业务模块,让模块重新导入新的session引用:

# tests/conftest.py
import importlib
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy import create_engine
import pytest
from unittest.mock import patch

# 导入所有依赖session的业务模块
import app.user.users
import app.articles.articles

def mocked_session():
    return scoped_session(sessionmaker(
        bind=create_engine('sqlite:///:memory:'),
        autocommit=False,
        autoflush=False
    ))

@pytest.fixture(autouse=True)
def mock_all_sessions():
    with patch('utils.sessions.session', new=mocked_session()) as mock_session:
        # 重新加载模块,使其引用新的session
        importlib.reload(app.user.users)
        importlib.reload(app.articles.articles)
        yield mock_session

这种方法需要维护所有依赖session的模块列表,模块较多时会比较繁琐,仅作为临时过渡方案。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 03:40:21