Fixture markers¶
testsuite.fixture_markers lets plugins and tests attach typed metadata
to fixture functions and later collect the fixtures that carry a given
type and are visible to the current pytest request.
The helpers are framework-agnostic: they only depend on pytest fixture definitions. userver uses them for service dependencies and static config patches; other stacks can reuse the same marks for Postgres initializers, mock setup, and similar discovery.
Marking a fixture¶
Apply the mark to the original function, under @pytest.fixture:
import dataclasses
import pytest
from testsuite import fixture_markers
@dataclasses.dataclass(frozen=True)
class PgInitializer:
order: int
def pg_init(*, order: int = 0):
def decorator(function):
return fixture_markers.mark(
function,
PgInitializer(order=order),
)
return decorator
@pytest.fixture
@pg_init()
def init_users(pgsql):
pgsql['mydb'].execute('INSERT INTO users ...')
@pytest.fixture(name='init_orders')
@pg_init(order=1)
def _init_orders(pgsql):
pgsql['mydb'].execute('INSERT INTO orders ...')
Querying marks¶
get_infos(request, info_type) returns a dict of fixture name to
info for fixtures that:
are visible to
request(plugin, ancestor conftest, this module, this class);have a mark of
info_type;have a scope at least as wide as
request.scope.
initializers = fixture_markers.get_infos(request, PgInitializer)
for name, _info in sorted(
initializers.items(),
key=lambda item: item[1].order,
):
request.getfixturevalue(name)
Overriding a marked fixture¶
An override inherits the mark. get_infos looks at the definition
pytest would call. When that definition has no mark of the requested
type, the mark comes from the nearest overridden definition that has
one. A mark on the override replaces the inherited value. An override
cannot drop the mark.
# conftest.py
@pytest.fixture
@pg_init(order=1)
def init_users(pgsql):
pgsql['mydb'].execute('INSERT INTO users ...')
# test_users.py
@pytest.fixture
def init_users(init_users, pgsql):
init_users
pgsql['mydb'].execute('INSERT INTO guests ...')
initializers = fixture_markers.get_infos(request, PgInitializer)
# {'init_users': PgInitializer(order=1)}
Utilities for attaching typed marks to pytest fixture functions and querying those marks during a test session.
Use mark() to store an info object on the original function.
Use get_infos() to retrieve fixtures tagged with a particular
info type among those visible to a request.
Marks are independent of @pytest.fixture: apply the mark to the
original function, then wrap with @pytest.fixture.
- testsuite.fixture_markers.get_infos(request: FixtureRequest, info_type: type[I], /) dict[str, I][source]
Return marked fixtures of info_type that are visible to a request.
Walks fixture definitions known to pytest, keeps those applicable to the requesting test, and skips fixtures whose scope is narrower than
request.scope.The winning fixture definition is the one pytest would call. If it has no mark of info_type, the mark is inherited from the nearest overridden definition that has one. A mark on the winner replaces the inherited mark. There is no way to drop an inherited mark.
See Fixture markers for usage examples.
- Parameters:
request – The pytest fixture request object.
info_type – The info class whose tagged fixtures you want to look up.
- Returns:
dict[str, I]mapping each tagged fixture’s name to the info object that was passed tomark(). The dict is a fresh copy; mutating it has no effect on stored data.
- testsuite.fixture_markers.mark(func: Callable[[...], object], info: object, /) Callable[[...], object][source]
Attach info to the original function func.
Creates the marks dict if it is missing, otherwise updates it. The info type is the lookup key for
get_infos(). Each type may be attached at most once.func must be the original function, not a
@pytest.fixturewrapper. Apply the mark under@pytest.fixture.See Fixture markers for usage examples.
- Parameters:
func – The original fixture function.
info – Metadata instance to store.
- Returns:
func, unchanged.