如何在Python中仅Mock类的非顶级调用以测试批量执行方法
实现指定方法真实执行、内部子方法Mock的测试方案
核心需求
测试Foo类的apply_all方法时,让apply_all本身真实执行,但其内部调用的所有apply_*(除apply_all外)方法替换为Mock,以便验证这些方法的调用情况。
解决方案一:动态替换实例方法
通过inspect模块筛选目标方法,批量将实例上的apply_*方法替换为Mock,保留apply_all的真实实现:
from unittest.mock import MagicMock import inspect class Foo: def apply_goal_filter(self): print("真实执行goal过滤") def apply_gender_filter(self): print("真实执行gender过滤") def apply_age_filter(self): print("真实执行age过滤") def apply_all(self): self.apply_goal_filter() self.apply_gender_filter() self.apply_age_filter() def test_apply_all(): foo = Foo() # 筛选所有以apply_开头、且不是apply_all的方法 target_methods = [ name for name, _ in inspect.getmembers(Foo, predicate=inspect.isfunction) if name.startswith("apply_") and name != "apply_all" ] # 批量替换为Mock并缓存 mock_map = {} for method_name in target_methods: mock = MagicMock() mock_map[method_name] = mock setattr(foo, method_name, mock) # 真实调用apply_all foo.apply_all() # 验证每个Mock方法都被调用一次 for method, mock in mock_map.items(): mock.assert_called_once() test_apply_all()
解决方案二:自定义包装类拦截属性访问
通过包装类统一处理实例的属性访问,仅对指定方法放行真实调用,其余apply_*返回Mock:
from unittest.mock import MagicMock class MockWrapper: def __init__(self, instance, keep_real_methods): self.instance = instance self.keep_real = keep_real_methods self.mock_cache = {} def __getattr__(self, name): # 保留指定方法的真实实现 if name in self.keep_real: return getattr(self.instance, name) # 对apply_开头的方法返回Mock(缓存避免重复创建) if name.startswith("apply_"): if name not in self.mock_cache: self.mock_cache[name] = MagicMock() return self.mock_cache[name] # 其他属性/方法正常转发 return getattr(self.instance, name) def test_apply_all_with_wrapper(): foo = Foo() # 包装实例,指定apply_all为真实执行的方法 wrapped_foo = MockWrapper(foo, keep_real_methods=["apply_all"]) # 调用真实的apply_all wrapped_foo.apply_all() # 验证Mock方法的调用情况 wrapped_foo.mock_cache["apply_goal_filter"].assert_called_once() wrapped_foo.mock_cache["apply_gender_filter"].assert_called_once() wrapped_foo.mock_cache["apply_age_filter"].assert_called_once() test_apply_all_with_wrapper()
为什么Mock(wraps=foo)不满足需求
Mock(wraps=foo)会将所有调用转发给原实例,仅记录调用行为,不会替换方法为Mock实现,因此内部的apply_*方法仍会真实执行,无法达到只Mock子方法的目的。
内容的提问来源于stack exchange,提问作者salius
相关产品推荐
相关产品推荐

