如何让Mock对象捕获所有方法调用并执行统一逻辑如调用计数
可以实现,我们可以利用unittest.mock.Mock动态生成属性的特性,通过自定义Mock子类,统一给所有动态生成的可调用属性(即方法)绑定相同的响应逻辑,不需要逐个配置每个方法的mock规则。
实现方案
1. 基础实现(用全局计数器)
from unittest import mock call_counter = 0 # 统一的响应逻辑:累加计数器,也可以自定义返回值、其他副作用 def common_side_effect(*args, **kwargs): global call_counter call_counter += 1 # 如果你需要所有方法返回统一值,直接在这里return即可,比如 return True class UnifiedMock(mock.Mock): def __getattr__(self, name): # 调用父类方法获取动态生成的属性 attr = super().__getattr__(name) # 只给非私有、可调用的属性绑定统一副作用 if callable(attr) and not name.startswith("_"): # 避免重复绑定副作用 if not hasattr(attr, "_side_effect_set"): attr.side_effect = common_side_effect attr._side_effect_set = True return attr
2. 优化实现(无全局变量,计数器封装在Mock实例内)
from unittest import mock class UnifiedCountMock(mock.Mock): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # 计数器作为实例属性,不需要全局变量 self.total_call_count = 0 def _count_call(self, *args, **kwargs): self.total_call_count += 1 # 按需添加统一返回逻辑 def __getattr__(self, name): attr = super().__getattr__(name) if callable(attr) and not name.startswith("_"): if not hasattr(attr, "_count_bound"): attr.side_effect = self._count_call attr._count_bound = True return attr
适配你的测试代码
替换你原来的mock创建逻辑即可,不需要修改其他测试代码:
def test_EventListener(): # 用自定义的UnifiedMock替换原有的普通Mock fooMock = UnifiedMock() # 如果用优化版的实现,直接初始化即可:fooMock = UnifiedCountMock() eventListener = EventListener(fooMock) # 执行你的测试用例 eventListener.consume(evt1) eventListener.consume(evt2) # ... 其他调用 # 基础实现可以直接读全局的call_counter print(f"总调用次数:{call_counter}") # 优化版实现可以读实例属性 # print(f"总调用次数:{fooMock.total_call_count}")
原理说明
unittest.mock.Mock在访问不存在的属性时,会自动创建一个新的Mock实例作为返回值。我们重写__getattr__方法,在返回动态生成的属性前,判断如果是可调用的方法类属性,就统一绑定我们预设的副作用逻辑,不管实际调用的是func_one还是func_two,或者Foo类未来新增的其他方法,都会自动触发统一的响应规则。
内容的提问来源于stack exchange,提问作者LeanMan
相关产品推荐
相关产品推荐

