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

使用SqlAlchemy Automap生成模型的查询函数单元测试报错及测试实现疑问

SqlAlchemy Automap生成模型的查询函数单元测试报错及测试实现疑问

我来帮你一步步梳理和解决这些测试问题~


一、解决ArgumentError: Column expression...报错

问题根源

你在测试中用mocker.patch('some_path.main.Model1')把整个Model1替换成了MagicMock对象,导致Model1.CustID变成了一个Mock实例。而SqlAlchemy的select()方法需要接收真实的Column表达式,Mock对象根本不满足SqlAlchemy的语法校验,所以抛出了这个错误。

而且你使用的UnifiedAlchemyMagicMock本身就是专门用来模拟SqlAlchemy Session的工具,完全不需要额外Patch模型类,这属于画蛇添足的操作。

修正后的测试代码

去掉不必要的Model1 Patch,直接用UnifiedAlchemyMagicMock的模拟能力即可:

from mock_alchemy.mocking import UnifiedAlchemyMagicMock
from main import get_info
import pytest

@pytest.fixture
def session():
    return UnifiedAlchemyMagicMock()

def test_info(session):
    # 直接配置Session mock的返回值
    session.execute.return_value.scalar.return_value = 1
    result = get_info(session, 1)
    assert result == 1

为什么返回None的测试能通过?

你提到把返回值换成None测试就通过了,是因为那个测试里你去掉了mocker.patch('some_path.main.Model1'),此时Model1是真实的类,Model1.CustID是合法的Column对象,SqlAlchemy能正常解析查询语法,再加上Session的mock返回None,断言自然能通过。


二、测试复杂查询时,是否需要创建本地模型?

你在test_new_main.py里纠结是否要创建本地Model1,其实分两种场景来看:

场景1:仅做单元测试(验证函数逻辑)

如果你只是想验证函数的返回值是否符合预期,继续用UnifiedAlchemyMagicMock模拟Session就行,完全不需要创建本地模型或真实数据库,直接配置mock返回值即可:

def test_get_expense_type_id(session):
    # 配置mock返回预期的ExpID
    session.execute.return_value.scalar.return_value = 2
    result = get_expense_type_id(session, 1)
    assert result == 2

    # 可选:验证查询是否符合预期(比如检查filter条件)
    session.execute.assert_called_once()
    called_select = session.execute.call_args[0][0]
    assert Model1.ExpID in called_select.columns

场景2:做集成测试(验证查询语法逻辑)

如果你想验证查询的实际逻辑是否正确(比如notin_过滤是否生效、多条件组合是否正确),那创建本地模型+内存数据库的方式非常合适,这能让测试更贴近真实生产场景。

注意修正测试代码里的小问题:你写的ExpenseReportItemType应该是你定义的Model1,修正后的代码如下:

from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import sessionmaker, DeclarativeBase
from new_main import get_expense_type_id
import pytest

class Base(DeclarativeBase):
    pass

class Model1(Base):
    __tablename__ = 'tbl_dummy'
    ExpID = Column(Integer, primary_key=True)
    CustID = Column(Integer)
    Type = Column(String(30))
    Name = Column(String(30))

@pytest.fixture(scope='session')
def test_session():
    engine = create_engine('sqlite+pysqlite:///:memory:', echo=True, future=True)
    Base.metadata.create_all(engine)
    Session = sessionmaker(bind=engine)
    return Session()

def test_get_expense_type_id(test_session):
    with test_session as s:
        # 插入测试数据:一条符合条件,一条会被notin过滤
        s.add_all([
            Model1(CustID=1, Type="LineItem", Name="Name1", ExpID=2),
            Model1(CustID=1, Type="LineItem", Name="a", ExpID=3)
        ])
        s.commit()
        
        # 调用待测试函数
        result = get_expense_type_id(s, 1)
        
        # 断言结果:只有Name不在排除列表的那条数据会被返回
        assert result == 2

总结

  • 单元测试:用UnifiedAlchemyMagicMock模拟Session,快速验证函数返回逻辑,无需真实数据库。
  • 集成测试:用内存数据库+本地模型,验证查询语法和过滤逻辑的正确性,测试更严谨。

备注:内容来源于stack exchange,提问作者Manika Midha

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.14 15:50:28