我想用尽可能少的样板来模拟我的一个功能。
在我的(简化的)项目中,我有以下文件/函数:
utils.py
,带有函数get_id(param1, param2)
(这就是我想嘲笑的)work.py
具有功能 do_work()
导入和使用 utils.get_id
tests/test_work.py
进行以下测试:from work import do_work
def test_work():
# somehow have get_id patched
...
do_work()
...
从
get_id
模块修补 work
非常容易。请注意,无论最终解决方案是什么,参数都很重要,因此 return_value
不起作用。
from work import do_work
import mock
def mock_id(param1, param2): return f"{param1} {param2}"
def test_work():
with mock.path("work.get_id", side_effect=mock_id):
...
do_work()
...
# A variation is:
@patch("work.get_id", side_effect=mock_id)
def test_another_work(mock_id):
...
确实有效,但有很多样板:
mock_id()
patch
行,并且可能需要测试函数的无用参数get_id()
conftest.py
和全局夹具我可以添加一个
conftest.py
文件,一劳永逸地定义 pytest 夹具
@pytest.fixture()
def _get_id():
def mock_get_id(param1, param2):
return f"{param1} {param2}"
with mock.patch("utils.get_id", side_effect=mock_get_id):
yield
我想我可以这样写我的测试:
@pytest.mark.usefixtures("_get_id")
def test_work():
...
我明确地不希望它与
autouse=True
一起使用,而这一条 @pytest.mark.usefixtures("_get_id")
行在我看来像是样板文件和明确性之间的良好平衡。
环顾四周,这看起来也可以起作用:
@pytest.fixture()
def _get_id(monkeypatch):
def mock_get_id(param1, param2):
return f"{param1} {param2}"
monkeypath("utils.get_id", mockget_id):
固定装置被调用、使用,但始终使用原始 get_id,而不是模拟版本。如何确保
get_id
已全局修补?
在 Python 中,当使用
from foo import get_id
将函数导入到多个位置时,通常不可能将其替换为另一个函数。 (请参阅 unittest.mock
的文档中的“修补位置”。)(从技术上讲,您可以非常棘手地用另一个对象替换函数的内部代码对象,但这会危险地进入黑魔法领域。)
如果这是关于单个
get_id
,我建议将其做成蹦床:
_test_get_id = None
def get_id(x):
if _test_get_id: # For ease of testing
return _test_get_id(x)
... # usual implementation
现在,您可以使用任何您想要设置
_test_get_id
的方法(例如,仅 monkeypatch.setattr
),所有对 get_id
的调用,无论它们可能已被导入,最终都将使用 _test_get_id
。