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

Flask-SQLAlchemy 3.0查询Mock始终返回None的单元测试问题

Flask-SQLAlchemy 3.0 Mock查询返回None的解决办法

问题场景

在Flask应用中Mock Flask-SQLAlchemy 3.0的查询时,明明给Mock的查询链赋值了User实例,但代码里的user变量始终返回None。

测试用例代码:

class TestVerify(TestCase):

    @patch("middleware.operations.models.user.User")
    def test_verify_token_request(self, user_query_mock: MagicMock) -> None:
        user_query_mock.query.return_value.filter_by.return_value.first.return_value = (
            User(user_id=4)
        )

        def verified_func(*args, **kwargs) -> bool:
            del args, kwargs
            return True

        with app.test_request_context(
            data=json.dumps({"user_id": 123}), content_type="application/json"
        ):
            response = verify_token_request(verified_func)
            result = response()  
            print(result)

被测试的装饰器函数:

def verify_token_request(func: Callable) -> Callable:

    @wraps(func)
    def wrapper(*args, **kwargs) -> str:
        user = User.query.filter_by(user_id=request.json["user_id"]).first()
        print(user)
        return func(*args, **kwargs)

    return wrapper

问题原因

Flask-SQLAlchemy 3.x里,User.query不是直接挂在User类上的属性,而是由项目里的SQLAlchemy实例(一般叫db)提供的查询描述符。你现在patch的是模型模块里的User类,但实际代码中调用的User.query会指向db.session.query(User)这条链,所以你的Mock设置根本没作用到实际执行的查询上。

解决办法

办法1:Patch正确的User.query路径

要Patch被测试函数(装饰器)所在模块中导入的User类的query属性,而不是模型原模块里的User。比如如果装饰器verify_token_request在middleware.operations.auth模块,就patch这个模块里的User.query:

# 替换成你装饰器所在的实际模块路径
@patch("middleware.operations.auth.User.query")
def test_verify_token_request(self, user_query_mock: MagicMock) -> None:
    # 直接给query链的返回值赋值
    user_query_mock.filter_by.return_value.first.return_value = User(user_id=4)

    def verified_func(*args, **kwargs) -> bool:
        del args, kwargs
        return True

    with app.test_request_context(
        data=json.dumps({"user_id": 123}), content_type="application/json"
    ):
        response = verify_token_request(verified_func)
        result = response()  
        print(result)

办法2:Mockdb.session.query

既然User.query本质上等价于db.session.query(User),直接patchdb.session的query方法也能解决:

from middleware.operations.models import db

@patch.object(db.session, 'query')
def test_verify_token_request(self, query_mock: MagicMock) -> None:
    mock_user = User(user_id=4)
    query_mock.return_value.filter_by.return_value.first.return_value = mock_user

    # 后续测试代码和之前一致

办法3:用内存数据库替代Mock

如果不想折腾Mock,Flask-SQLAlchemy支持直接用内存SQLite数据库做测试,直接插入测试数据更直观:

def test_verify_token_request(self) -> None:
    # 配置内存数据库
    app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:'
    db.init_app(app)
    
    with app.app_context():
        db.create_all()
        # 添加测试用户
        test_user = User(user_id=123)
        db.session.add(test_user)
        db.session.commit()

        def verified_func(*args, **kwargs) -> bool:
            del args, kwargs
            return True

        with app.test_request_context(
            data=json.dumps({"user_id": 123}), content_type="application/json"
        ):
            response = verify_token_request(verified_func)
            result = response()  
            print(result)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 07:27:06