简体   繁体   中英

Adding pytest markers to test depending on used fixtures

I am looking for a way to mark pytest tests with markers depending on which fixtures this test uses.

I would like to use this to filter tests based on the features they use. For example: A test that uses the "database_connection" fixture should automatically be marked with "database". This way I could include or exclude all such tests based on whether or not the database credentials are at hand.

This is what I currently have:

def pytest_configure(config):
    """Register hook to extend the list of available markers."""
    config.addinivalue_line("markers", "database: mark test that need a database connection.")

def pytest_collection_modifyitems(config, items):  # pylint: disable=unused-argument
    """Register hook to map used fixtures to markers."""
    for item in items:
        if "database_connection" in item.fixturenames:
            database_marker = pytest.mark.database()
            item.add_marker(database_marker)

@pytest.fixture
def database_connection():
    """Fixture providing a database connection."""

This works just the way I want but I dislike having to maintain the mapping from fixture to mark separated from the fixture itself. What I'd like to do is decorate the fixtures with the markers that should be set on all tests that use them. It should look like this:

def pytest_configure(config):
    """Register hook to extend the list of available markers."""
    config.addinivalue_line("markers", "database: mark test that need a database connection.")

def pytest_collection_modifyitems(config, items):  # pylint: disable=unused-argument
    """Register hook to map used fixtures to markers."""
    for item in items:
        for fixture in item.fixtures:
            item.add_markers(fixture.markers)         

@pytest.fixture(markers=["database"])
def database_connection():
    """Fixture providing a database connection."""

Of course I could build a decorator that stores the mapping in a global variable:

_fixture_marker_map = {}

def set_markers(*markers):
    def decorator_set_markers(func):
        _fixture_marker_map[func.__name__] = markers
        
        @functools.wraps(func)
        def wrapper_set_markers(*args, **kwargs):
            return func(*args, **kwargs)
        return wrapper_set_markers
    return decorator_set_markers

@set_markers("database")
@pytest.fixture
def database_connection():
    """Fixture providing a database connection."""

However, this feels a bit hacky. I'm pretty sure this is not an odd usecase and there might be some pytest feature that already provides what I need.

Does anyone have an idea how to implement this in a simple manner?

There's a simple solution that seems to work. The trick is to notice that you can parametrize fixtures and mark the parameters. You can just ignore the parameter. For example:

@pytest.fixture(params=[pytest.param(None, marks=pytest.mark.db))
def database_connection(request):
    """Fixture providing a database connection."""
    ...


def test_db_connection(database_connection):
    ...

will automatically mark any test that consumes the database_connection with the db mark.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM