Python单元测试如何mock/patch被模拟类内的多个方法
问题根因
你的测试代码存在4个核心问题:
- 直接
patch了整个DataProcess类,把类替换成了MagicMock对象,类里原本写的run_task、validate_gathered_data等真实业务逻辑完全被架空,执行run_task()时只是调用了mock的空方法,根本不会走真实的调用链,validate_gathered_data自然不会被触发。 - 给
gather_data设置返回值时,传入的是self.__get_mocked_dataset方法本身,没有加括号调用拿到实际的DataFrame,所以报错信息里会显示传入的是bound method对象。 - 测试文件导错了异常类名,源码里定义的是
DataProcessException,你导入的RawDataException根本不存在。 - 代码里写错了方法名,把
save_output_data写成了save_output_artifact;另外类中的辅助方法__get_mocked_dataset漏写了self参数,调用时会抛参数不匹配错误。
可行解决方案
核心原则:不要mock整个被测类,只需要mock掉会触发外部依赖的部分,保留被测业务逻辑的真实执行流程即可,以下提供两种常用写法:
写法1:直接替换实例方法(最易上手)
实例化真实的DataProcess对象,仅替换掉会触发数据库、存储服务调用的两个方法,用spy包裹校验方法保留原有逻辑:
from unittest.mock import Mock import pandas as pd import pytest # 修正异常类导入 from src.main.run_data_process import DataProcess, DataProcessException class TestDataProcess: # 补全self参数 def __get_mocked_dataset(self): return pd.DataFrame( [[111, 222, 333], ['text_1', 'text_2', 'text_3'], [True, False, False]], columns=['col_1', 'col_2', 'col_3']) def test_gathered_data_is_validated_successfully(self): # 实例化真实业务对象 dp = DataProcess() test_df = self.__get_mocked_dataset() # 仅mock会触发外部依赖的方法 dp.gather_data = Mock(return_value=test_df) dp.save_output_data = Mock(return_value=None) # 用spy包裹校验方法,不改变原有执行逻辑,仅记录调用情况 dp.validate_gathered_data = Mock(side_effect=dp.validate_gathered_data) # 执行真实业务流程 dp.run_task() # 断言 dp.validate_gathered_data.assert_called_once_with(test_df) dp.save_output_data.assert_called_once_with(test_df)
写法2:用patch.object规范打补丁
用unittest自带的patch.object上下文管理器做补丁,测试结束后会自动还原方法,不会污染其他测试用例:
from unittest.mock import patch import pandas as pd import pytest from src.main.run_data_process import DataProcess, DataProcessException class TestDataProcess: def __get_mocked_dataset(self): return pd.DataFrame( [[111, 222, 333], ['text_1', 'text_2', 'text_3'], [True, False, False]], columns=['col_1', 'col_2', 'col_3']) def test_gathered_data_is_validated_successfully(self): test_df = self.__get_mocked_dataset() dp = DataProcess() with patch.object(dp, 'gather_data', return_value=test_df), \ patch.object(dp, 'save_output_data', return_value=None), \ patch.object(dp, 'validate_gathered_data', wraps=dp.validate_gathered_data) as mock_validate: dp.run_task() mock_validate.assert_called_once_with(test_df) dp.save_output_data.assert_called_once_with(test_df)
最佳实践补充
更推荐直接mock最外层的外部依赖,不需要修改类内部方法,可以完整覆盖所有内部逻辑的测试:
def test_gathered_data_is_validated_successfully(self): test_df = self.__get_mocked_dataset() # 注意mock路径是被测文件中引用依赖的路径,不是依赖的原始导入路径 with patch('src.main.run_data_process.DBConnection') as mock_db, \ patch('src.main.run_data_process.DataStorageManager') as mock_storage: mock_db.return_value.run.return_value = test_df dp = DataProcess() dp.run_task() # 可以完整校验全链路调用 mock_db.return_value.run.assert_called_once() mock_storage.return_value.save_output_object.assert_called_once_with(test_df)
内容的提问来源于stack exchange,提问作者anajbellini
相关产品推荐
相关产品推荐

