如何针对类中特定方法Mock/Patch其内部调用的方法?
解决方案:基于调用栈的条件Mock
要实现仅当bar_method被method_that_use_bar_function调用时Mock,其他场景保留真实逻辑,可以通过检查调用栈区分调用来源,结合unittest.mock的side_effect实现条件化Mock。
实现步骤
1. 编写条件化Mock的副作用函数
利用inspect模块获取调用栈,识别发起调用的上层方法,决定返回Mock值还是执行真实逻辑:
import inspect from src.bar import Bar def conditional_bar_method(): # 遍历调用栈,跳过当前函数和Mock自身的栈帧 for frame_info in inspect.stack()[1:]: caller_func = frame_info.function # 判断调用来源 if caller_func == "method_that_use_bar_function": return "mocked_result" elif caller_func == "method_that_use_bar_function_too": # 执行真实的bar_method逻辑 return Bar().bar_method() # 其他未知调用场景默认返回真实结果 return Bar().bar_method()
2. 编写测试用例
使用unittest.mock.patch替换Bar.bar_method,并将上述副作用函数传入:
from unittest.mock import patch from runner import method_to_run_all_my_code def test_conditional_mock(): with patch("src.bar.Bar.bar_method", side_effect=conditional_bar_method): final_result = method_to_run_all_my_code() # 验证结果是否符合预期:第一个调用Mock,第二个调用真实方法 assert final_result == "Use 1 - mocked_resultUse 2 - real method"
原理说明
inspect.stack()返回当前程序的调用栈列表,每个元素包含调用方的函数名、代码位置等信息。- 通过遍历栈帧,可精准识别
Foo类的哪个方法调用了bar_method,从而执行对应逻辑。 - 这种方式无需修改业务代码,完全在测试层实现条件Mock。
备选方案(允许修改业务代码时)
如果可以微调业务代码,也可以通过给bar_method传递标识参数区分调用场景:
修改src/foo.py:
class Foo: def __init__(self) -> None: self.bar = Bar() def method_that_use_bar_function(self): # 传递调用场景标识 result = self.bar.bar_method(caller="create_user") print("[Foo] Use 1 - " + result) return "Use 1 - " + result def method_that_use_bar_function_too(self): result = self.bar.bar_method(caller="get_user") print("[Foo] Use 2 - ", result) return "Use 2 - " + result
修改src/bar.py:
class Bar: def bar_method(self, caller=None): print("[Bar] this is a real method of bar") return "real method"
测试用例中通过参数判断Mock:
from unittest.mock import patch from src.bar import Bar from runner import method_to_run_all_my_code def test_conditional_mock_with_param(): def side_effect(caller=None): if caller == "create_user": return "mocked_result" else: return Bar().bar_method(caller) with patch("src.bar.Bar.bar_method", side_effect=side_effect): final_result = method_to_run_all_my_code() assert final_result == "Use 1 - mocked_resultUse 2 - real method"
这种方式更直观,性能也更好,但需要调整业务代码,适合可修改代码的场景。
内容的提问来源于stack exchange,提问作者natielle
相关产品推荐
相关产品推荐

