单元测试Mock SQLAlchemy Session上下文管理器后,目标函数未使用内存数据库会话的问题排查与解决
单元测试Mock SQLAlchemy Session上下文管理器后,目标函数未使用内存数据库会话的问题排查与解决
我来帮你分析下问题出在哪,以及怎么修复:
问题根源拆解
你当前的mock方式有两个核心问题:
- Mock上下文管理器的姿势不对:你直接给
mock_session.__enter__赋值Mock对象,但patch出来的Mock是一个可调用对象——当代码执行with Session()时,实际上是先调用Session()得到上下文管理器实例,再调用这个实例的__enter__方法。你直接修改mock_session的__enter__属性,并没有影响到Session()调用后返回的实例的__enter__逻辑,所以目标函数里的会话还是空的mock对象,根本没连上你的内存数据库。 - 测试数据添加的方式错误:你用
with mock_session as session:来添加测试数据,这里的session是mock出来的假对象,不是你创建的内存会话,所以数据根本没真正写入到内存数据库里。
具体修复步骤
1. 正确初始化内存数据库与会话
首先要把内存数据库的会话和原代码的Session上下文管理器解耦,自己创建独立的会话工厂:
@patch("app.helpers.db_helper.Session") def test_get_users_to_query(self, mock_session_ctx) -> None: # 1. 创建内存SQLite引擎 self.engine = create_engine("sqlite:///:memory:") # 2. 创建内存数据库专属的会话工厂 self.session_factory = sessionmaker(bind=self.engine) # 3. 生成实际的内存会话 self.db_session = self.session_factory() # 4. 创建所有表结构 Base.metadata.create_all(self.engine)
2. 往内存会话里添加测试数据
直接操作自己的内存会话,把测试数据真正写入内存库:
# 添加测试用户到内存会话 fake_user1 = User(user_name="jdb1", flag=False) fake_user2 = User(user_name="jdb2", flag=True) self.db_session.add(fake_user1) self.db_session.add(fake_user2) self.db_session.commit()
3. 正确Mock Session上下文管理器
让被patch的Session()上下文管理器,在进入时返回你的内存会话:
# 关键:让Session()调用返回的上下文管理器,__enter__方法返回内存会话 # Session() -> 上下文管理器实例 -> __enter__() -> 返回我们的内存会话 mock_session_ctx.return_value.__enter__.return_value = self.db_session # 可选:设置__exit__方法避免报错 mock_session_ctx.return_value.__exit__.return_value = None
4. 调用目标函数并验证结果
注意原函数get_users_to_query的默认flag参数是False,如果要查flag=True的用户,记得传参:
# 调用目标函数,查询flag=True的用户 users_to_query = get_users_to_query(flag=True) # 验证结果 print("Users:", users_to_query) # 断言验证(可选,更规范的测试方式) self.assertEqual(len(users_to_query), 1) self.assertEqual(users_to_query[0]["user_name"], "jdb2")
额外注意点
- 确保patch的路径绝对正确:必须是目标函数
get_users_to_query导入Session的路径(也就是app.helpers.db_helper.Session),路径错了mock完全不会生效。 - 原代码里的
User模型要正确继承自Base,否则Base.metadata.create_all不会创建users表。
备注:内容来源于stack exchange,提问作者abinitio




