Search code examples
pythonpytest

Select and/or deselect tests that use a particular fixture?


I see that there are some questions about conditionally skipping tests with certain fixtures: Is there a way to skip a pytest fixture? and Skip tests using a particular pytest fixture.

However I am looking for a straightforward way to select or deselect tests that use a particular fixture. I'd like to avoid manually marking all such tests, because the fixture might be used indirectly, and this is generally error-prone and not scalable.

Ideally, I'd like to be able to run something like pytest -m 'not uses_foo', and all tests that require the foo fixture would be deselected.

Again, this question is not about skipping the tests. I am specifically asking about a way to make them (de-)selectable as one might normally be able to do with marks.


Solution

  • I'm not aware of a builtin mechanism, but you can easily implement it yourself.

    The pytest_collection_modifyitems hook is run after test collection, at which point all fixtures have been resolved. The names of all fixtures requested by a test can be retrieved via the fixturenames attribute. In the same hook, you can mark the tests with a skip marker or remove the tests you don't want to run entirely. If you want them to appear as deselected, you can call the pytest_deselected hook from this hook.

    To make it configurable from the command line, you can use the pytest_addoption hook.

    Example conftest.py:

    import pytest
    
    def pytest_addoption(parser):
        parser.addoption("--skip-fixtures", default=None)
    
    def pytest_collection_modifyitems(config, items):
        skip_fixtures_opt = config.option.skip_fixtures
        if not skip_fixtures_opt:
            return
    
        skipped_fixtures = skip_fixtures_opt.split(",")
        selected = list(items)
        deselected = []
    
        for test_item in items:
            for fixture in skipped_fixtures:
                if fixture in test_item.fixturenames:
                    selected.remove(test_item)
                    deselected.append(test_item)
    
        items[:] = selected
        config.hook.pytest_deselected(items=deselected)
    

    If you run pytest --skip-fixtures "foo,bar", all tests using foo or bar will be deselected. If you want to skip them instead, add a skip marker via test_item.add_marker(pytest.mark.skip("reason..."))