unittest中通过变量引用调用方法时.called断言失效如何解决
问题原因与结论
这是预期行为,核心问题出在unittest.mock.patch的工作原理和你的函数注册时机不匹配:
patch的本质是临时修改指定作用域下的标识符指向,你在测试中@patch("submethod_b")修改的是当前模块里submethod_b这个名称的引用,替换为mock对象- 你的
_method_registry是模块加载阶段就完成初始化的,里面存储的是原始submethod_b函数的引用,和你后续patch替换的submethod_b标识符完全无关,所以main_method从注册表取到的始终是原函数,mock自然不会被标记为已调用 - 改成if-else直接调用时,每次执行都会去当前作用域查找
submethod_b标识符,这时候已经被patch替换为mock对象,所以测试能正常通过
可行的解决方法
- 方案1:直接patch注册表中的对应值
不需要修改业务代码,只需要把测试的patch目标改成注册表内的对应条目即可,示例:@patch("your_module._method_registry['b']") def test_correct_method_and_arguments(mocked_submethod_b): main_method("b") assert mocked_submethod_b.called - 方案2:修改注册表存储逻辑,动态获取函数
把注册表存储的内容从函数引用改成函数名,调用时动态获取,这样patch就能正常生效:# 业务代码修改 _method_registry = { "a": "submethod_a", "b": "submethod_b", } def main_method(key): method_to_use = globals()[_method_registry[key]] method_to_use()
重构为类实现的方案评估
这个方案完全可行,而且比现有实现更易维护和测试:
你可以定义抽象基类声明公共方法,不同业务逻辑对应不同的实现类,main_method根据key获取对应类的实例调用方法即可。这种方式不仅测试时patch类/实例方法更灵活,后续新增业务逻辑也只需要新增实现类,不需要修改原有注册和调度逻辑,符合开闭原则。
内容的提问来源于stack exchange,提问作者Daniel
相关产品推荐
相关产品推荐

