如何在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
测试新思路
直接断言SQL正确性:别只打印,直接把生成的SQL和预期字符串做断言,比如:
assert raw_sql == "select user.id from user where user.name='asdfg'"这样能确保你的查询逻辑没写错,不会生成奇怪的SQL。
用内存测试库代替Mock:如果场景允许,直接用SQLite内存数据库跑测试,让查询真实执行,同时通过ORM的事件监听抓SQL。这种方式更贴近真实环境,能发现Mock漏掉的问题。
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
相关产品推荐
相关产品推荐

