pytest:如何使用标记注入夹具?

时间:2018-05-29 21:57:11

标签: python hook pytest fixtures

我正在编写一个带有灯具的pytest插件,该灯具具有设置一些理想模拟的副作用。我想编写一个简单的标记,允许用户在测试运行之前调用此夹具设置,而不必将夹具包含在测试功能参数中 - 基本上,"注入"使用标记的夹具。我的理由是用户可能想要模拟设置而不需要夹具本身的返回值,在这种情况下,使用标记对我来说似乎更直观,而不是要求他们声明他们赢得使用的夹具不能使用

我如何使用标记来要求pytest中的夹具?查看文档,似乎我想要挂钩pytest_collection_modifyitems之类的东西,使用Item.iter_markers检查每个项目上的相关标记,然后以某种方式更新灯具列表。但是,阅读代码时,我无法确定如何准确触发该夹具设置。

以下是有关灯具的简化示例:

@pytest.fixture
def mocks(mocker):
    ret_val = 10
    mocker.patch('path.to.patch', return_value=ret_val)
    return ret_val

以下是用户现在可以设置模拟的内容:

def test_mocks(mocks):
    # 'path.to.patch' will be mocked in this test
    # ... test code ...

但是,如果可以通过标记触发灯具,那么测试可能会是什么样子:

@pytest.mark.mocks
def test_mocks():
    # 'path.to.patch' will be mocked in this test, too
    # ... test code ...

2 个答案:

答案 0 :(得分:3)

使用usefixtures标记:

# conftest.py
import pytest

@pytest.fixture
def mocks(mocker):
    mocker.patch('os.path.isdir', return_value=True)

# test_me.py
import os
import pytest

@pytest.mark.usefixtures('mocks')
def test_mocks():
    assert os.path.isdir('/this/is/definitely/not/a/dir')

传递多个灯具也是可行的:

@pytest.mark.usefixtures('mocks', 'my_other_fixture', 'etc')

但是,有一点需要注意:代码中的mocks fixture返回值ret_val。通过测试函数args传递夹具时,此值将在mocks arg下返回;当你使用标记时,你不再拥有arg,所以你不能使用这个值。如果您需要模拟值,请在测试函数args中传递fixture。还有其他一些可以想象的方法,比如通过缓存传递ret_val,然而,生成的代码会更复杂,更不易读,所以我不会打扰。

答案 1 :(得分:1)

我能够通过使用pytest_collection_modifyitems hook在收集后调整测试项目上的灯具列表来实现这一点:

@pytest.hookimpl(trylast=True)
def pytest_collection_modifyitems(items):
    '''
    Check if any tests are marked to use the mock.
    '''
    for item in items:
        # Note that `get_marker` has been superceded by `get_closest_marker`
        # in the development version of pytest
        if item.get_marker('mocks'):
            item.fixturenames.append('mocks')

收集后调整Item.fixturenames列表会以我希望的方式触发灯具设置。

如果您不关心使用自定义标记,@ hoefling建议使用内置usefixtures标记也是一个很好的解决方案。