【发布时间】:2025-11-27 14:35:01
【问题描述】:
我开始使用 python mock library 进行测试。我想模拟一个在被测模块的命名空间中导入的模块,而不实际导入它或要求它首先存在(即抛出 ImportError)。
假设存在以下代码:
foo.py
import helpers
def foo_func():
return helpers.helper_func()
目标是在“helpers.py”在任何地方都不存在时测试 foo_func(),如果它存在,就当它不存在。
第一次尝试,使用超酷的@patch 装饰器:
from mock import patch, sentinel
import foo
@patch("foo.helpers")
def foo_test(mock):
mock.helper_func.return_value = sentinel.foobar
assert foo.foo_func() == sentinel.foobar
如果可以导入“帮助器”模块,则此方法有效。如果它不存在,我会收到 ImportError。
下一次尝试使用补丁,无装饰器:
from mock import patch, sentinel, Mock
import foo
helpers_mock = patch("foo.helpers")
helpers_mock.start()
def foo_test():
helpers_mock.helper_func = Mock('helper_func')
helpers_mock.helper_func.return_value = sentinel.foobar
assert foo.foo_func() == sentinel.foobar
同样,如果缺少“帮助者”,这将不起作用......并且,如果存在,则断言失败。不太清楚为什么会这样。
第三次尝试,当前解决方案:
import sys
helpers_mock = Mock(name="helpers_mock", spec=['helper_func'])
helpers_mock.__name__ = 'helpers'
sys.modules['helpers'] = helpers_mock
import foo
def foo_test():
helpers_mock.helper_func.return_value = sentinel.foobar
assert foo.foo_func() == sentinel.foobar
无论“helpers.py”是否存在,此测试都会通过。
这是实现这一目标的最佳方式吗?我正在使用的模拟库是否提供了替代方案?我还有什么其他方法可以做到这一点?
【问题讨论】:
-
虽然我完全支持单元测试,但我认为在这种情况下,您正在测试语言的一个特性,而不是代码的有用功能。您还可以在 foo.py 中修复您的导入,以便使用导入保护不需要某些猴子修补(例如:尝试:导入助手,除了 ImportError:foo.py 中的 helpers = None 而不是裸导入)。我的意思是现在 foo.py 不是用 a 编写的,以允许缺少帮助程序,并且允许这样做应该会大大简化您的测试。
-
我真的很喜欢 import guard 的想法,它解决了很多问题。如果我按照您的建议使用导入保护,我可以使用我给出的第一个示例(@patch 装饰器)。谢谢!
标签: python module import mocking