Python中如何通过Patch测试函数外的对象实例化
解决模块导入时执行代码的Mock Patch问题
问题场景
你的bar.py在模块级别直接执行了函数调用和对象实例化:
from foo import foo # function that returns an int from dog import Dog dog_foo = 4 * foo() my_dog = Dog(dog_foo) ...
这类代码会在第一次导入bar模块时立即执行,导致你之前尝试的两种patch方式都无法生效——要么patch时机不对,要么patch的位置错误。
正确的Patch方式
核心要做到两点:
- 在
bar模块的代码执行前让patch生效; - 针对
bar模块内的foo引用进行patch(而非原foo模块的foo); - 若模块已被缓存,需重新加载模块触发代码重新执行。
情况1:首次导入bar(无模块缓存)
直接在patch上下文内导入bar,确保模块级代码执行时mock已生效:
from unittest.mock import patch with patch("bar.foo") as mock_foo: # 设置mock返回值 mock_foo.return_value = 5 # 首次导入bar,此时模块级代码会使用mock后的foo import bar # 验证结果 assert bar.dog_foo == 20 # 4 * 5 = 20 # 假设Dog类将传入参数保存到了self.value属性,验证实例化正确性 assert bar.my_dog.value == 20
情况2:bar已被导入(存在模块缓存)
如果测试代码或其他用例已经导入过bar,需要用importlib.reload()重新加载模块,让模块级代码重新执行:
from unittest.mock import patch import importlib import bar # 之前已导入过bar,模块被缓存 with patch("bar.foo") as mock_foo: mock_foo.return_value = 5 # 重新加载bar模块,触发模块级代码再次执行 bar = importlib.reload(bar) # 验证结果 assert bar.dog_foo == 20 assert bar.my_dog.value == 20
为什么之前的方式无效?
- patch
foo.foo无效:当bar中使用from foo import foo时,会把foo.foo的引用复制到bar模块的命名空间中成为bar.foo。后续patch原foo.foo不会改变bar内已存在的引用,所以模块级代码还是会调用原函数。 - patch时机错误:如果在patch生效前已经导入过
bar,模块缓存会直接返回已执行过代码的模块对象,后续的patch无法影响已经实例化的my_dog。
内容的提问来源于stack exchange,提问作者user7097722
相关产品推荐
相关产品推荐

