如何根据测试的夹具特征动态地将新夹具添加到测试中

时间:2017-10-16 16:11:05

标签: python pytest

所以我想要实现的是使用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_MOCKSBUILTIN_MOCKS列表的用法。所以基本上在pytest钩子中我应该能够看到有一个mock_foo夹具,但它还没有注册,所以我用工厂创建了一个模拟并注册它。我无法弄清楚如何做到这一点。基本上我正在调查pytest_runtest_setup函数,但无法弄清楚如何进行实际的夹具注册。所以基本上我想知道哪个钩子/调用可以从这个钩子以编程方式注册新的夹具函数。

1 个答案:

答案 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挂钩对我来说也是一个好地方,只要我尝试过。

注意在这种情况下你没有注册灯具。对于夹具配准来说为时已晚,因为所有夹具都是在收集/参数化阶段更早收集和准备的。在此阶段,您只能执行测试并提供值。您有责任计算夹具值并在之后销毁它们。