所以我想要实现的是使用pytest自动模拟各种模块中的函数。所以我在conftest.py中定义了这个:
import sys
import __builtin__
from itertools import chain
# Fixture factory magic START
NORMAL_MOCKS = [
"logger", "error", "logging", "base_error", "partial"]
BUILTIN_MOCKS = ["exit"]
def _mock_factory(name, builtin):
def _mock(monkeypatch, request):
module = __builtin__ if builtin else request.node.module.MODULE
ret = Mock()
monkeypatch.setattr(module, name, ret)
return ret
return _mock
iterable = chain(
((el, False) for el in NORMAL_MOCKS),
((el, True) for el in BUILTIN_MOCKS))
for name, builtin in iterable:
fname = "mock_{name}".format(name=name)
_tmp_fn = pytest.fixture(name=fname)(_mock_factory(name, builtin))
_tmp_fn.__name__ = fname
setattr(
sys.modules[__name__],
"mock_{name}".format(name=name), _tmp_fn)
# Fixture normal factory magic END
这有效,但我想省略NORMAL_MOCKS
和BUILTIN_MOCKS
列表的用法。所以基本上在pytest钩子中我应该能够看到有一个mock_foo
夹具,但它还没有注册,所以我用工厂创建了一个模拟并注册它。我无法弄清楚如何做到这一点。基本上我正在调查pytest_runtest_setup
函数,但无法弄清楚如何进行实际的夹具注册。所以基本上我想知道哪个钩子/调用可以从这个钩子以编程方式注册新的夹具函数。
答案 0 :(得分:1)
其中一种方法是在收集/生成阶段,即在测试执行开始之前参数化测试:https://docs.pytest.org/en/latest/example/parametrize.html
# conftest.py
import pytest
def mock_factory(name):
return name
def pytest_generate_tests(metafunc):
for name in metafunc.fixturenames:
if name.startswith('mock_'):
metafunc.parametrize(name, [mock_factory(name[5:])])
# test_me.py
def test_me(request, mock_it):
print(mock_it)
一个非常简单的解决方案。但缺点是,当测试实际上不是时,测试报告为参数化:
$ pytest -s -v -ra
====== test session starts ======
test_me.py::test_me[it] PASSED
====== 1 passed in 0.01 seconds ======
要在没有参数化的情况下完全模拟函数args,你可以做一个不那么明显的技巧:
# conftest.py
import pytest
def mock_factory(name):
return name
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_protocol(item, nextitem):
for name in item.fixturenames:
if name.startswith('mock_') and name not in item.funcargs:
item.funcargs[name] = mock_factory(name[5:])
yield
pytest_runtest_setup
挂钩对我来说也是一个好地方,只要我尝试过。
注意在这种情况下你没有注册灯具。对于夹具配准来说为时已晚,因为所有夹具都是在收集/参数化阶段更早收集和准备的。在此阶段,您只能执行测试并提供值。您有责任计算夹具值并在之后销毁它们。