如何Mock多个pd.read_sql数据库调用并解决测试时No module named 'pd'报错
问题原因
unittest.mock.patch 接收的目标路径是Python模块的真实导入路径,你在测试代码里用pd作为pandas的别名属于当前测试文件的局部别名,mock模块找不到全局名为pd的模块,因此抛出ModuleNotFoundError。
解决方法
有两种可行的修正方式,二选一即可:
方式1:直接patch pandas原生的read_sql方法
把装饰器的路径改成pandas官方模块路径:
@mock.patch('pandas.read_sql')
方式2:patch业务代码中实际调用的pd.read_sql
如果你只想针对该业务模块的read_sql调用做mock,需要写你业务模块里pd.read_sql的完整导入路径,假设你的get_sql_queries函数定义在functions.get_transaction_data模块中,且该模块头部是用import pandas as pd导入的,就写成:
@mock.patch('functions.get_transaction_data.pd.read_sql')
修正后的完整测试代码示例
import unittest from unittest import mock import pandas as pd from functions import get_transaction_data class GetSQLQueriesTest(unittest.TestCase): # 这里修改为正确的patch路径 @mock.patch('pandas.read_sql') def test_get_sql_queries(self, mock_read_sql): transaction_data = pd.DataFrame(columns=['Fund', 'Symbol'], data=[['Fund 1', 'Symbol 1']]) mark_data = pd.DataFrame(columns=['Fund', 'Symbol'], data=[['Fund 2', 'Symbol 2']]) mock_read_sql.side_effect = (transaction_data, mark_data) # 调用顺序固定时可使用该写法 output = get_transaction_data.get_transactions_between_two_dates() # 注意对比DataFrame要改用pandas自带的方法,直接用assertEqual会报错 pd.testing.assert_frame_equal(output[0], transaction_data) pd.testing.assert_frame_equal(output[1], mark_data)
额外优化建议
如果你的函数中SQL查询顺序可能调整,用side_effect按顺序返回的方式不够稳妥,可以自定义判断逻辑匹配SQL语句返回对应值:
def mock_read_sql_side_effect(sql, con): if "sql_table" in sql: return pd.DataFrame(columns=['Fund', 'Symbol'], data=[['Fund 1', 'Symbol 1']]) elif "other_sql_table" in sql: return pd.DataFrame(columns=['Fund', 'Symbol'], data=[['Fund 2', 'Symbol 2']]) raise ValueError("未匹配到测试SQL") mock_read_sql.side_effect = mock_read_sql_side_effect
内容的提问来源于stack exchange,提问作者Mike
相关产品推荐
相关产品推荐

