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

如何在Mock数据库连接的测试中查看生成的原始SQL语句?

Mock数据库时怎么看生成的原始SQL?

为啥现在看不到真实SQL?

你直接用MagicMock把整个数据库连接mock掉后,代码里的查询对象都变成了MagicMock实例,自然拿不到真实生成的SQL。核心思路是:让查询的构建逻辑正常跑,只mock数据库的执行环节,别把查询对象本身给mock没了。

具体实现方法

假设你的代码是用SQLAlchemy这类ORM写的,给你两种可行方案:

先看你的示例代码结构(参考)

db.py

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

engine = create_engine("sqlite:///test.db")
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

users.py

from sqlalchemy.orm import Session
from models import User

def get_user_by_name(db: Session, name: str):
    return db.query(User.id).filter(User.name == name).first()

原来的测试写法(只会输出MagicMock)

from unittest.mock import MagicMock
from users import get_user_by_name

def test_get_user_by_name():
    mock_db = MagicMock()
    result = get_user_by_name(mock_db, "asdfg")
    print(mock_db.query)  # 全是MagicMock,看不到SQL

方案一:利用ORM自带的编译能力抓SQL

让ORM正常构建查询,只mock执行方法,然后把查询对象编译成真实SQL:

from unittest.mock import patch, MagicMock
from users import get_user_by_name

def test_get_user_by_name_capture_sql():
    # 模拟Session,但让query返回真实的查询对象
    with patch("db.SessionLocal") as mock_session:
        mock_db = mock_session.return_value
        mock_query = MagicMock()

        # 拦截query调用,捕获真实查询并编译成SQL
        def capture_real_query(*args):
            real_query = args[0]
            # 把查询编译成带真实参数的SQL
            raw_sql = str(real_query.compile(compile_kwargs={"literal_binds": True}))
            print(f"生成的SQL: {raw_sql}")  # 就能看到select user.id from user where user.name='asdfg'
            return mock_query
        
        mock_db.query.side_effect = capture_real_query
        # 模拟查询链的调用(filter、first)
        mock_query.filter.return_value = mock_query
        mock_query.first.return_value = 1

        result = get_user_by_name(mock_db, "asdfg")
        assert result == 1

方案二:自己写Mock查询类拼接SQL

如果不想依赖ORM的编译能力,自己写个类模拟查询链,跟踪调用过程拼接SQL:

from unittest.mock import MagicMock
from users import get_user_by_name

class MockQuery:
    def __init__(self):
        self.sql_filters = []
    
    def filter(self, condition):
        # 把ORM的条件转成SQL片段(这里简单处理,实际可以根据框架调整解析逻辑)
        cond_str = str(condition).replace("User.", "user.")
        self.sql_filters.append(f"where {cond_str}")
        return self
    
    def first(self):
        # 拼接完整SQL
        full_sql = f"select user.id from user {' '.join(self.sql_filters)}"
        print(f"生成的SQL: {full_sql}")
        return 1

def test_get_user_by_name_custom_mock():
    mock_db = MagicMock()
    # 让db.query返回我们自己的MockQuery实例
    mock_db.query.side_effect = lambda *args: MockQuery()
    
    result = get_user_by_name(mock_db, "asdfg")
    assert result == 1

测试新思路

  1. 直接断言SQL正确性:别只打印,直接把生成的SQL和预期字符串做断言,比如:

    assert raw_sql == "select user.id from user where user.name='asdfg'"
    

    这样能确保你的查询逻辑没写错,不会生成奇怪的SQL。

  2. 用内存测试库代替Mock:如果场景允许,直接用SQLite内存数据库跑测试,让查询真实执行,同时通过ORM的事件监听抓SQL。这种方式更贴近真实环境,能发现Mock漏掉的问题。

  3. ORM事件监听抓SQL:比如SQLAlchemy可以加个事件监听,不用改业务代码就能捕获所有执行的SQL:

    from sqlalchemy import event
    from db import engine
    
    def log_sql(conn, clauseelement, *args):
        raw_sql = str(clauseelement.compile(compile_kwargs={"literal_binds": True}))
        print(f"执行的SQL: {raw_sql}")
    
    # 注册监听,测试时自动打印SQL
    event.listen(engine, "before_execute", log_sql)
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 15:36:33