Fixture-aware pytest support¶
BetterPy makes pytest fixtures behave like first-class symbols in PyCharm. Plain PyCharm can navigate and complete many Python names, but fixture names are not ordinary imports or local variables: pytest resolves them through test location, class context, conftest.py ancestry, overrides, plugin loading, usefixtures, getfixturevalue, indirect parametrization, and built-in fixtures.
The Pytest fixture engine is the shared model behind BetterPy's fixture navigation, completion, type inference, Find Usages, rename support, and fixture inspections. It is enabled by default and can be toggled in Settings -> Tools -> BetterPy -> Testing (Pytest) -> Fixtures.
What BetterPy adds over plain PyCharm¶
BetterPy resolves fixture names with pytest's effective visibility rules instead of treating every same-named parameter as a plain Python parameter.
- A fixture parameter goes to the fixture pytest would actually inject at that location.
- Fixture completion includes visible fixtures from the current module, classes, ancestor
conftest.pyfiles, pytest plugin modules, installed pytest plugins, and common pytest built-ins. - Rename and Find Usages include injected parameters and string-based fixture requests instead of stopping at the fixture function declaration.
- Type inference for fixture parameters uses the resolved fixture return or yield value.
- Fixture inspections reuse the same resolution model, so warnings account for overrides, autouse fixtures, plugins, and dynamic fixture requests.
Resolution that follows pytest¶
BetterPy understands the fixture shapes that appear in real test suites:
- Same-module fixtures, class fixtures, nested class fixtures, and fixtures from inherited test classes.
- Nearest-scope wins between class, module, local
conftest.py, and parentconftest.pydeclarations. @pytest.fixture(name="...")and assignment-style fixtures such asfixture_name = pytest.fixture()(_factory).- Top-level imperative registrations such as
pytest.fixture(name="fixture_name")(_factory). - Fixture dependencies that intentionally request an outer fixture with the same name while overriding it.
autouse=True, declared fixturescope=, and static or dynamicparams=.- uv workspace member boundaries, so a
conftest.pyfrom another workspace member does not leak into the current member. - Common pytest built-ins including
request,tmp_path,tmpdir,monkeypatch,capsys,capfd,caplog,recwarn,pytestconfig, andcache.
Example:
# tests/conftest.py
import pytest
@pytest.fixture
def user():
return User(role="default")
# tests/admin/conftest.py
@pytest.fixture
def user():
return User(role="admin")
# tests/admin/test_permissions.py
def test_permissions(user):
assert user.role == "admin"
In tests/admin/test_permissions.py, Go to Declaration on user opens the admin fixture, not the parent fixture.
Dynamic fixture strings¶
Fixture names in strings become real references when pytest treats the string as a fixture request:
@pytest.mark.usefixtures("db")- module or class
pytestmark = pytest.mark.usefixtures("db") request.getfixturevalue("db")@pytest.mark.parametrize("db", [...], indirect=True)@pytest.mark.parametrize(("db", "case"), [...], indirect=["db"])
That means navigation, completion, rename, and unknown-fixture warnings work in string literals too. Direct parametrization stays separate: a plain @pytest.mark.parametrize("case", [...]) argument is treated as a test parameter, not as a fixture.
Example:
@pytest.fixture
def seeded_user():
return UserFactory.create()
@pytest.mark.usefixtures("seeded_user")
def test_profile_page():
...
Renaming seeded_user updates the fixture declaration and the usefixtures string.
Plugin fixtures¶
BetterPy includes fixtures loaded through pytest plugin mechanisms, so plugin fixtures show up in the same editor workflows as project fixtures.
pytest_plugins = ["tests.plugins.auth"]in a rootconftest.py.- Installed pytest plugins exposed through
pytest11entry points. - Entry points declared in
pyproject.tomlorsetup.cfg. - Fixtures imported or re-exported from plugin modules.
- Assignment-style plugin fixtures where the injected name differs from the helper function name.
Project fixtures still win over plugin fixtures with the same name, matching pytest's closest-wins behavior. BetterPy also highlights plugin-provided fixture usage so it is easier to see when a parameter comes from a plugin rather than from local test code.
Editor workflows¶
The engine powers the normal PyCharm actions you already use:
Fixture Editor Integration metadata links: PY-87756, PY-81495, PY-60973, PY-56268, PY-57812, PY-63269, PY-61634, PY-89790, PY-49850, PY-59721, PY-53176, PY-61040, PY-88555, PY-60421.
- Go to Declaration from fixture parameters and dynamic fixture strings.
- Go to Type Declaration from a fixture parameter or usage to the fixture that supplies the value.
- Go to Super from an overriding fixture to the parent fixture it shadows.
- Show Implementations from a parent fixture to narrower fixtures that override it.
- Gutter markers for fixture override relationships.
- Completion in test and fixture parameter lists,
usefixtures,getfixturevalue, and indirect parametrization. - Rename from fixture declarations, injected parameters, and supported dynamic strings.
- Find Usages that reports effective fixture consumers and respects search scope, fixture overrides, plugin fixtures, and dynamic string requests.
- Fixture refactorings that reuse this model include Inline pytest fixture (PY-66243) and Push Down pytest fixture (PY-56186).
BetterPy also keeps false positives down. It ignores self, cls, lambda parameters, directly parametrized test arguments, parameters injected by mock.patch, and functions whose non-pytest decorators make fixture injection ambiguous.
Type inference and documentation¶
Fixture parameter types come from the resolved fixture, not from the parameter name.
- Return fixtures type the injected parameter as the returned value.
- Yield fixtures annotated as
Iterator[T],Iterable[T],Generator[T, ...], and async variants type the injected parameter asT. - Async fixtures unwrap awaited return values.
- Built-in fixtures carry known types where BetterPy has a safe catalog entry.
- Shared fixtures can narrow dependency types by consumer scope when local overrides change what pytest injects.
- Quick Documentation can show fixture types, returned literals, dataclass fields, Pydantic fields, TypedDict fields, and attribute docstrings. Related YouTrack issue: PY-66245.
Example:
@pytest.fixture
def api_token() -> str:
return "token"
def test_client(api_token):
reveal_type(api_token) # str
See also: Pytest fixture documentation.
Fixture inspections¶
Several BetterPy inspections are only useful because they share the same fixture model:
- Unknown fixture parameters and unknown dynamic fixture strings.
- Uninjected fixture references inside tests or fixtures. Related YouTrack issues: PY-71966, PY-89003.
- Unused fixtures, including fixtures used through
usefixtures,getfixturevalue, indirect parametrization, autouse, plugin loading, or another fixture. - Scope mismatches where a broader-scoped fixture depends on a narrower-scoped fixture.
- Fixture annotation mismatches where a test or fixture parameter annotation disagrees with the resolved fixture value type.
- Fixture override contract mismatches where an overriding fixture produces an incompatible value type.
request.paramreads in fixtures that have noparams=and are not reached by indirect parametrization.pytest_pluginsassignments in non-rootconftest.pyfiles, which pytest ignores.- PyCharm inspection suppression for valid pytest patterns such as fixture method overrides and parametrized classes.
Related pages:
- Fixture Editor Integration
- Pytest fixture scope inspection
- Unused pytest fixture inspection
- Pytest fixture direct import inspection
- pytest_plugins in non-root conftest inspection
Performance model¶
The engine is index-backed and cache-aware. BetterPy indexes fixture declarations, uses word-search-backed consumer lookup for usages and inspections, limits conftest.py fixture searches to the subtree where pytest can see them, and caches pytest root/config and plugin fixture discovery.
This matters most in large test suites: opening a conftest.py, running inspections, or finding usages of a shared fixture should not require parsing every test file in the project.