Search code examples
pythonhookpytestfixtures

pytest: how to use a mark to inject a fixture?


I'm writing a pytest plugin with a fixture that has a side effect of setting up some desirable mocks. I'd like to write a simple mark that will allow the user to call this fixture setup before the test runs, without having to include the fixture in the test function parameters -- essentially, "injecting" the fixture using a mark. My reasoning is that the user may want the mocked setup without needing the return value of the fixture itself, in which case it seems more intuitive to me to use a mark than to require that they declare a fixture they won't be using.

How might I use a mark to require a fixture in pytest? Looking at the docs, it seems like I want to hook into something like pytest_collection_modifyitems, check for the relevant mark on each item using Item.iter_markers, and then somehow update the list of fixtures. Reading the code, however, I'm having trouble figuring out how exactly to trigger that fixture setup.

Here's a simplified example of what the fixture in question looks like:

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

Here's what the user can do to set up the mocks now:

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

But here's what the test might look like if the fixture could be triggered via a mark instead:

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

Solution

  • I was able to implement this by using the pytest_collection_modifyitems hook to adjust the list of fixtures on the test item after collection:

    @pytest.hookimpl(trylast=True)
    def pytest_collection_modifyitems(items):
        '''
        Check if any tests are marked to use the mock.
        '''
        for item in items:
            if item.get_closest_marker('mocks'):
                item.fixturenames.append('mocks')
    

    Adjusting the Item.fixturenames list after collection appears to trigger the fixture setup in the way I was hoping.

    If you don't care about using a custom mark, however, @hoefling's recommendation to use the built-in usefixtures mark would be a good solution, too.