Testing (Pytest)¶
BetterPy's largest feature group — 49 features that supercharge your pytest workflow with fixture management, fixture inspections, parametrize helpers, mocking support, assertions, and test tree navigation.
Fixtures¶
BetterPy provides first-class, deep integration for pytest fixtures, refactorings, inspections, and documentation directly inside the IDE.
Pytest fixture engine¶
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-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.
Start with fixture-aware pytest support for a release-oriented overview of everything the shared fixture engine adds over plain PyCharm: pytest-style resolution, plugin fixtures, dynamic fixture strings, fixture typing, rename, Find Usages, and fixture-specific inspections.
Fixture Editor Integration¶
Enables deep integration of pytest fixtures into the IDE editor, making fixture names behave like normal Python symbols. BetterPy resolves fixture definitions with pytest-style precedence, then uses that model for navigation, completion, definition searches, hierarchy actions, renaming, and usages.
For the broader release overview of the shared fixture model, plugin support, dynamic fixture strings, typing, and inspections, see fixture-aware pytest support.
Navigation¶
Fixture parameters resolve to the fixture definition that pytest would choose for that location.
# tests/conftest.py
@pytest.fixture
def user():
return User("default")
# tests/api/conftest.py
@pytest.fixture
def user():
return User("api")
# tests/api/test_users.py
def test_profile(user):
assert user.name == "api"
In test_profile, Go to Declaration (⌘B / Ctrl+B) on user navigates to tests/api/conftest.py.
Typed parameter implementations¶
Go to Implementations (⌥⌘B / Ctrl+Alt+B) on a typed function parameter opens the concrete implementations of its declared class. This works for ordinary functions as well as pytest fixtures.
class Handler(ABC):
@abstractmethod
def handle(self): ...
class SyncHandler(Handler):
def handle(self): ...
class AsyncHandler(Handler):
def handle(self): ...
def dispatch(handler<caret>: Handler): ...
Invoking Go to Implementations on handler opens Choose Implementation of inferred type Handler and offers SyncHandler and AsyncHandler. The same inferred-type chooser is available on an injected fixture parameter when its fixture function declares that return type.
Completion¶
Fixture-name completion is available in test and fixture parameter lists. Suggestions include visible fixtures from the current file, imports, ancestor conftest.py files, and discovered plugin fixtures.
Cold completion returns available local or cached suggestions without waiting for complete discovery. Builtin, plugin, and parent-override fixtures may arrive in the cache later: continue typing or invoke completion again to see them. An untouched popup is not updated automatically, and background discovery never reopens a dismissed popup.
Completion can suggest client and inserts it as a normal parameter. BetterPy skips names that are already controlled by @pytest.mark.parametrize, and it keeps self and cls out of fixture suggestions.
Rename And Usages¶
Renaming a fixture updates fixture parameters and known fixture references rather than only the Python function declaration. Find Usages (⌥F7 / Alt+F7) also treats fixture injections as usages, so a fixture can be audited before changing or deleting it.
@pytest.fixture
def api_client():
return Client()
def test_status(api_client):
assert api_client.get("/status").ok
Renaming api_client updates both the fixture definition and the injected parameter.
Override Hierarchy Support¶
Fixture definitions participate in IDE hierarchy workflows:
- Go to Super from an overriding fixture opens the fixture it shadows in a broader scope.
- Gutter markers on overridden fixtures list narrower-scope fixtures that override them.
- Go to Type Declaration from a fixture parameter opens the fixture function that supplies the value.
- Implement/Override Methods can offer fixture overrides when editing test classes that inherit fixture methods.
Fixture Injection Intentions¶
Provides intentions to easily convert between explicit fixture parameters and @pytest.mark.usefixtures decorators.
Convert Between Parameter And Decorator Injection¶
Two intentions help switch between explicit fixture parameters and @pytest.mark.usefixtures.
Use the decorator form when a fixture is needed only for setup or teardown side effects. Use the parameter form when the test reads the fixture value.
Create pytest fixtures from parameters¶
Create pytest fixtures from parameters generates a fixture for an unresolved pytest fixture parameter.
How to use it¶
Place the caret on an unresolved fixture parameter in a test or fixture function, then run BetterPy: Create pytest fixture ....
When it helps¶
Use this quick fix when writing tests top-down and adding fixture parameters before defining the fixtures they refer to.
Notes¶
- BetterPy creates a
@pytest.fixturefunction in the appropriate scope and importspytestwhen needed. - The feature is gated by the Create pytest fixtures from parameters setting under Settings | BetterPy | Testing (Pytest) | Fixtures.
Pull Up pytest fixture¶
Place the caret in a fixture and choose Refactor | Pull Members Up.... A class fixture becomes module-scoped, while a fixture in a nested conftest.py moves to the parent conftest.py, widening the tests that can request it.
For example, class TestSuite: @pytest.fixture def db(self): ... becomes top-level @pytest.fixture def db(): ...; test methods keep requesting db. Required imports and referenced helpers move with the fixture, while conflicts leave both files unchanged.
A fixture in a test module can also move into conftest.py in the same directory. BetterPy creates the target conftest when needed and blocks the move if it would collide with an existing fixture name.
Push Down pytest fixture¶
Place the caret in a fixture and choose Refactor | Push Members Down.... A module fixture can move into a test class, while a fixture in a parent conftest.py moves or copies into an immediate sibling pytest test module or a selected child conftest.py, narrowing where pytest exposes it.
For example, with tests/move/conftest.py defining tomove and the only consumer in tests/move/test_.py, BetterPy moves @pytest.fixture def tomove(): ... into test_.py and removes it from conftest.py. A fixture consumed by both api/ and ui/ can instead move or copy into selected child conftests. BetterPy keeps the source when consumers remain outside the selected exact file or recursive child-directory scope.
When consumers span several destinations and the fixture scope permits copying, BetterPy can populate all consumer destinations automatically. Otherwise, it offers a destination chooser. Sibling-module fixtures are visible only in that file; child-conftest fixtures are visible throughout that child directory. Conflicting destinations are excluded, and if none remain, no files are changed.
Pytest fixture documentation¶
Enhances Quick Documentation and member type inference for pytest fixtures across files.
Quick Documentation on a fixture or fixture parameter shows the resolved fixture type, dataclass/Pydantic/TypedDict fields, attribute docstrings, and literal values returned or yielded by simple fixtures. List, tuple, set, and dict literals are rendered across multiple lines so nested fixture data is easier to scan.
Supported fixture cases:
- Primitive return types such as
str,bool,list,dict,set,int, andfloat. - Generator-style fixtures annotated as
Generator[T, ...],Iterator[T],Iterable[T], and their async variants; BetterPy documents and infers the yielded value typeT. - Dataclasses, Pydantic models, and TypedDicts, including inherited fields, default values, and attribute docstrings.
- Fixtures defined in
conftest.py, including fixtures whose returned class is declared inside the fixture body. - The built-in
requestfixture, which is typed as_pytest.fixtures.SubRequestwhen available.
Parametrized test arguments are ignored because they shadow fixtures with the same name.
How to invoke: F1 / Ctrl+Q on a fixture parameter.
Example:
import pytest
@pytest.fixture
def api_config() -> dict:
return {
"base_url": "https://api.example.test",
"headers": {"Accept": "application/json"},
"timeouts": [1, 5, 10],
}
def test_client(api_config):
...
Quick Documentation shows the fixture type and the returned dictionary value as a multiline block, including nested dictionaries and lists.
New pytest members¶
New pytest members adds Generate menu actions for creating pytest tests and fixtures in the current scope.
How to use it¶
Open a module or class matching the configured pytest collection naming, then run Generate | pytest test or Generate | pytest fixture.
When it helps¶
Use these actions when adding tests or fixtures near the caret without manually typing boilerplate or decorators.
Notes¶
- New test names and fixture names are generated uniquely for the current file or class.
- Generated tests and fixtures start as live templates so you can edit the name and body in place with
Tab. - New tests start with
assert False, "Not implemented"so they fail until you write the real assertion. - Fixture generation imports
pytestwhen needed. - Test and fixture generation are hidden when neither the current module nor the containing class matches the configured pytest collection naming.
- Test generation is hidden in
conftest.py, while fixture generation remains available there. - The feature is gated by the New pytest members setting under Settings | BetterPy | Testing (Pytest) | Fixtures.
Pytest hooks¶
BetterPy treats pytest hook functions as first-class symbols: it generates hook stubs where pytest actually discovers them, links implementations back to the hook specification, and shows the hook documentation inline.
Generate a hook implementation¶
Generate pytest hook inserts a hook implementation into the file pytest loads hooks from, such as the nearest conftest.py or a plugin module. The generated function is opened as a live template, so the parameter list and body can be adjusted immediately after insertion.
Navigation and documentation¶
- A gutter marker next to a hook implementation leads to the hook specification and to other implementations of the same hook.
- Hook navigation discovers implementations in files matched by the active pytest
python_filespatterns as well asconftest.pyand plugin modules. - Go to Declaration on a hook implementation in
conftest.pyresolves to the corresponding hook specification shipped with pytest. - Quick documentation renders the hook's documented contract, including its parameters and expected return value.
- Hook names are real references in
conftest.py, so renaming and Find Usages cover implementations acrossconftest.pyfiles.
Notes¶
- Hook generation follows pytest's own locations, including
conftest.pyancestry,pytest_pluginsdeclarations, and plugin modules. Navigation and reference-based rename/Find Usages currently apply toconftest.pyimplementations. - The feature can be toggled in Settings | Tools | BetterPy | Testing (Pytest) | Fixtures.
Fixture Inspections¶
Pytest fixture scope inspection¶
Pytest fixture scope inspection reports fixtures whose scope is narrower than a fixture they depend on.
How to use it¶
Open a pytest fixture that depends on another fixture with a broader scope. BetterPy highlights incompatible scope declarations.
When it helps¶
Use this inspection to catch scope mismatches before pytest fails collection or runtime fixture setup.
Notes¶
- BetterPy can offer a quick fix for compatible scope adjustments.
- The feature is gated by the Pytest fixture scope inspection setting under Settings | BetterPy | Testing (Pytest) | Fixture Inspections.
Pytest fixture to usefixtures inspection¶
Pytest fixture to usefixtures inspection reports unused setup-only fixture parameters in tests and offers conversion to @pytest.mark.usefixtures(...).
How to use it¶
Open a test function that declares a setup-only fixture parameter and never references it in the test body. @pytest.mark.usefixtures(...) is the right replacement only when the fixture does not return or yield a value. Fixtures that provide a value stay as parameters so the unused result remains visible.
When it helps¶
Use this inspection to make setup-only fixture dependencies explicit without keeping unused parameters in the test signature.
Notes¶
- BetterPy updates decorators and imports
pytestwhen needed. - Pytest built-in fixtures such as
capsys,tmp_path,monkeypatch, orpytesterare never reported. - The feature is gated by the Pytest fixture to usefixtures inspection setting under Settings | BetterPy | Testing (Pytest) | Fixture Inspections.
Pytest fixture redundant parameter inspection¶
Pytest fixture redundant parameter inspection reports fixture parameters that are already injected by pytest mechanisms.
How to use it¶
Open a test that declares a fixture parameter also supplied through autouse, usefixtures, or module-level pytestmark.
When it helps¶
Use this inspection to remove fixture parameters that are not referenced by the function body and only repeat existing injection.
Notes¶
- BetterPy avoids reporting parameters that are used in the function body.
- The feature is gated by the Pytest fixture redundant parameter inspection setting under Settings | BetterPy | Testing (Pytest) | Fixture Inspections.
Unused pytest fixture inspection¶
Unused pytest fixture inspection reports fixture functions that are not used by tests or other fixtures.
How to use it¶
Open a file containing pytest fixtures. BetterPy highlights fixtures that have no detected references.
When it helps¶
Use this inspection to remove dead fixture code and keep test support modules easier to navigate.
Notes¶
- BetterPy offers safe-delete for unused fixtures.
- The feature is gated by the Unused pytest fixture inspection setting under Settings | BetterPy | Testing (Pytest) | Fixture Inspections.
Pytest fixture direct import inspection¶
Pytest fixture direct import inspection reports fixture functions imported directly into conftest.py.
How to use it¶
Open a conftest.py file that imports fixture functions directly. BetterPy highlights imports that should be replaced with explicit plugin scoping.
When it helps¶
Use this inspection to keep fixture sharing explicit and avoid surprising fixture availability across test packages.
Notes¶
- Prefer
pytest_pluginswhen exposing fixtures from another module. - The feature is gated by the Pytest fixture direct import inspection setting under Settings | BetterPy | Testing (Pytest) | Fixture Inspections.
Fixture annotation mismatch inspection¶
Reports test or fixture parameters whose explicit type annotation does not match the value type provided by the resolved fixture.
Pytest fixture override contract inspection¶
Reports pytest fixture overrides whose produced value type is incompatible with the parent fixture they shadow.
Pytest redundant fixture import inspection¶
Reports explicitly imported pytest fixtures that are already auto-discoverable via conftest or plugins.
Examples¶
Redundant Import (Warning)¶
# conftest.py
import pytest
@pytest.fixture
def my_fixture():
return 42
# Warning: Fixture 'my_fixture' is already provided by pytest (conftest/plugins); the explicit import is redundant.
from conftest import my_fixture
def test_example(my_fixture):
assert my_fixture == 42
After Quick-Fix (Remove redundant fixture import)¶
request.param without params inspection¶
Reports fixtures that read request.param without declaring params= and without being targeted by indirect parametrization.
Unknown dynamic pytest fixture inspection¶
Reports unresolved string-based fixture requests in usefixtures, getfixturevalue, and indirect parametrization.
Pytest fixture uninjected reference inspection¶
Pytest fixture uninjected reference inspection reports fixture names referenced in a test or fixture without being declared as parameters.
How to use it¶
Open a pytest function that uses a fixture name directly without injecting it.
When it helps¶
Use this inspection to catch accidental global-looking fixture references that pytest will not inject automatically.
Notes¶
- BetterPy focuses on fixture functions and pytest test contexts.
- The feature is gated by the Pytest fixture uninjected reference inspection setting under Settings | BetterPy | Testing (Pytest) | Fixture Inspections.
@pytest.mark.usefixtures on fixture is a no-op inspection¶
@pytest.mark.usefixtures on fixture is a no-op inspection reports usefixtures decorators applied to fixture functions.
How to use it¶
Open a fixture function decorated with @pytest.mark.usefixtures(...). BetterPy highlights the decorator because pytest only applies it to tests.
When it helps¶
Use this inspection to find fixture setup assumptions that are silently ignored by pytest.
Notes¶
- Move the dependency into the fixture parameter list instead.
- The feature is gated by the @pytest.mark.usefixtures on fixture is a no-op inspection setting under Settings | BetterPy | Testing (Pytest) | Fixture Inspections.
pytest_plugins in non-root conftest inspection¶
pytest_plugins in non-root conftest inspection reports pytest_plugins assignments in non-root conftest.py files.
How to use it¶
Open a nested conftest.py file containing a pytest_plugins assignment. BetterPy highlights assignments that pytest will ignore.
When it helps¶
Use this inspection to move plugin declarations to the root conftest where pytest will honor them.
Notes¶
- BetterPy uses pytest configuration discovery to distinguish root and nested conftest files.
- The feature is gated by the pytest_plugins in non-root conftest inspection setting under Settings | BetterPy | Testing (Pytest) | Fixture Inspections.
Plugins¶
Pytest plugin references¶
Turns the module strings in a pytest_plugins assignment into real references, so conftest.py plugin wiring behaves like ordinary Python imports.
- ⌘+Click / Ctrl+Click a plugin string to jump to the plugin module.
- Renaming or moving a plugin module or its package updates the
pytest_pluginsstrings automatically. - An inspection reports plugin strings that do not resolve to a module, at error level.
# conftest.py
pytest_plugins = [
"plugins.database", # navigates to plugins/database.py
"plugins.typo", # Cannot resolve pytest plugin module 'plugins.typo'
]
Single strings, list entries, tuple entries, and parenthesized strings are all supported. Project modules win over same-named library modules, and a package wins over a same-named module. Dynamically built values and f-strings are left alone: they neither get references nor are reported as unresolved.
Configuration¶
Missing pytest test directory¶
Highlights a configured pytest testpaths directory that does not exist and offers to create the missing directory.
Parametrize¶
Parametrize pytest test¶
Parametrize pytest test adds, removes, completes, and maintains pytest.mark.parametrize parameters for pytest tests.
How to use it¶
Place the caret in a pytest test or parametrize decorator, then run the available BetterPy parametrize intention. Inside a parametrized test's function parameter list, invoke basic completion and select the aggregate suggestion to insert every missing static argname in decorator order.
When it helps¶
Use this feature when turning a single test into a parametrized test, adding another parameter to an existing decorator, inserting all missing function parameters at once, removing an unused parameter, or renaming parametrized arguments safely.
Notes¶
- The feature includes rename validation for parametrized argument names.
- BetterPy keeps function parameters and decorator argnames synchronized.
- Completion supports comma-separated strings, tuples of strings, lists of strings, and stacked decorators; dynamic argname expressions are left to normal completion.
- The feature is gated by the Parametrize pytest test setting under Settings | BetterPy | Testing (Pytest) | Parametrize.
Pytest parametrized case run gutter icon¶
This feature adds run gutter icons for individual pytest.mark.parametrize cases.
How to use it¶
Put each parametrized case on its own line, then use the gutter run icon beside the case to run only that pytest node. The regular run icon beside the test function remains available for running all of its cases, so you can switch between the whole test and an individual case.
Notes¶
- The line marker appears only when BetterPy can infer the exact pytest case ID.
- Explicit ids from
ids=[...]andpytest.param(..., id="...")are supported. - IDs inferred from structured values such as tuples, lists, and dictionaries are normalized to the node IDs pytest uses.
- The marker follows the configured
python_files,python_classes,python_functions, andtestpathscollection rules, including inherited test methods and explicit-path runs. - The feature is gated by the Pytest parametrized case run gutter icon setting under Settings | BetterPy | Testing (Pytest) | Parametrize.
Convert pytest.param¶
Convert pytest.param converts parametrized values between plain values and explicit pytest.param(...) calls.
How to use it¶
Place the caret inside a pytest.mark.parametrize values list, then run BetterPy: Convert argument to pytest.param() or the reverse conversion intention.
When it helps¶
Use this intention when adding or removing per-case metadata such as ids and marks while keeping the parameter value structure correct.
Notes¶
- BetterPy imports
pytestwhen converting plain values topytest.param(...). - Values with marks or ids are not converted back to plain values.
- The feature is gated by the Convert pytest.param setting under Settings | BetterPy | Testing (Pytest) | Parametrize.
Pytest parametrize count mismatch inspection¶
This inspection reports @pytest.mark.parametrize entries where the number of values does not match the number of argument names.
How to use it¶
Enable the inspection under Settings | BetterPy | Testing (Pytest) | Parametrize. BetterPy highlights mismatched parametrize rows in the editor.
Notes¶
- The inspection understands plain value rows and
pytest.param(...)rows. - It is gated by the Pytest parametrize count mismatch setting.
Pytest parametrize ids count inspection¶
This inspection reports ids=[...] lists whose length does not match the number of parametrized test cases.
How to use it¶
Enable the inspection under Settings | BetterPy | Testing (Pytest) | Parametrize. BetterPy highlights ids lists that do not line up with the parametrized values.
Notes¶
- Matching ids counts keep pytest output names predictable.
- The feature is gated by the Pytest parametrize ids count setting.
Pytest parametrize unused argname inspection¶
This inspection reports parametrized argument names that are not used by the target test body.
How to use it¶
Enable the inspection under Settings | BetterPy | Testing (Pytest) | Parametrize. BetterPy highlights unused argnames and the corresponding values.
Notes¶
- The inspection accounts for stacked parametrize decorators.
- It is gated by the Pytest parametrize unused argname setting.
Pytest parametrize unexpected keyword inspection¶
This inspection reports unsupported keyword arguments passed to pytest.mark.parametrize.
How to use it¶
Enable the inspection under Settings | BetterPy | Testing (Pytest) | Parametrize. BetterPy highlights misspelled or unsupported keyword names such as testids.
Notes¶
- Supported keyword arguments are
argnames,argvalues,indirect,ids,scope, and pytest's internal_param_markargument. - The feature is gated by the Pytest parametrize unexpected keyword setting.
Assertions & Test Editing¶
Use actual test outcome¶
Replaces assertion expected values with the actual value from the last failed test run.
Failed tests are matched with the python_files, python_classes, and python_functions rules from the pytest configuration that applies to their project root, so custom test names and multi-root projects are supported.
How to invoke: ⌥⏎ / Alt+Enter on a failed assertion → "BetterPy: Use actual test outcome"
Toggle pytest skip¶
Toggle pytest skip adds or removes pytest skip markers for tests, test classes, modules, and parameter values.
How to use it¶
Place the caret on a supported pytest target, then run BetterPy: Toggle pytest skip from the intention menu.
When it helps¶
Use this intention when triaging tests and you need to skip or unskip the current target without editing decorators manually.
Notes¶
- The intention supports functions, classes, module-level
pytestmark,pytest.param(...), and simple parameter values in parametrized tests. - The feature is gated by the Toggle pytest skip setting under Settings | BetterPy | Testing (Pytest) | Assertions & Test Editing.
Surround with pytest.raises()¶
Surround with pytest.raises() wraps selected test statements in a with pytest.raises(Exception): block.
How to use it¶
Select one or more statements in a pytest test, then use the Python surround action and choose pytest.raises().
When it helps¶
Use this surrounder when converting a failing statement into an explicit exception assertion while keeping indentation and imports handled for you.
Notes¶
- BetterPy adds a
pytestimport when needed. - The surrounder is available only in pytest test functions and avoids wrapping code already inside
pytest.raises. - The feature is gated by the Surround with pytest.raises() setting under Settings | BetterPy | Testing (Pytest) | Assertions & Test Editing.
Pytest failed line inspection¶
Marks the line where a pytest test failed, providing visual feedback directly in the editor.
Notes¶
- The highlight is based on the last pytest failure location remembered by BetterPy.
- The feature works together with Use actual test outcome when an assertion diff is available.
- The feature is gated by the Pytest failed line inspection setting under Settings | BetterPy | Testing (Pytest) | Assertions & Test Editing.
Pytest missing assert inspection¶
This inspection reports pytest tests that never assert anything, and tests that explicitly return a value instead of asserting. Both cases silently pass in pytest, so the test looks green while it verifies nothing.
How to use it¶
Enable the inspection under Settings | BetterPy | Testing (Pytest) | Code Inspections. BetterPy highlights the test name of a test without assertions, and the return <value> statement of a test that returns a value.
What counts as an assertion¶
BetterPy does not just look for a literal assert in the test body. A test is considered to assert something when it:
- contains a Python
assertstatement, - raises
AssertionErrorexplicitly, for exampleraise AssertionError("unexpected value"), - uses
pytest.raises(...),pytest.warns(...), orpytest.deprecated_call(), whether as a context manager or as a direct call such as the legacypytest.raises(ValueError, func, *args)form, - calls
pytest.fail(...),pytest.xfail(...), orpytest.skip(...)as the terminating check, - calls a
unittest.TestCase-style assertion onselforcls, such asself.assertEqual(...),self.assertTrue(...), orself.assertRaises(...), - calls a fluent assertion helper such as
assert_that(...)from assertpy or PyHamcrest, - calls a mock assertion method such as
assert_called_once_with(...)orassert_not_awaited()on a value BetterPy recognises as aMockorAsyncMock, - or calls a project helper function that itself asserts. Helper chains are followed across files in your own code, up to a bounded depth, and recursive helpers are handled without hanging.
All pytest forms are recognised through the same alias-aware resolution, so import pytest, import pytest as pt, from pytest import warns, and from pytest import raises as expect_error all work.
What is deliberately not reported¶
- Functions that pytest would not collect: test-named functions in non-test files, methods in classes that are not test classes, and helper functions or fixtures that happen to start with
test_. BetterPy honourspython_files,python_classes,python_functions, andtestpathsfrom your pytest configuration. - A bare
returnwithout a value, andreturnstatements inside nested functions. - Assertions inside a nested function that is never called — those do not satisfy the inspection, so such a test is still reported.
assert_-prefixed methods on ordinary objects, onMock(spec=...)where the spec class defines the method, or onMock(unsafe=True); only real mock assertion methods count.unittest-style assertions are only accepted in their camelCase form (assertEqual, notassert_equal) and only when called onselforcls.- Calls that cannot be resolved, for example helpers from third-party libraries — the test is reported, because BetterPy cannot see an assertion.
Notes¶
- The inspection reports at weak warning level and only runs on your own code, not on library sources.
- It is gated by the Pytest missing assert inspection setting.
Pytest mark completion¶
Pytest mark completion suggests known pytest markers in pytest.mark contexts.
How to use it¶
Invoke completion after pytest.mark. in a test file.
Where the markers come from¶
- Pytest's built-in markers, such as
skip,skipif,xfail,parametrize, andusefixtures. - Markers registered in your project configuration under
markers = ...inpyproject.toml,pytest.ini, orsetup.cfg. - Markers contributed by installed pytest plugins, taken from BetterPy's pytest collection snapshot.
Built-in and configured markers are suggested immediately, without waiting for a pytest collection run. Each lookup entry shows the marker description and, for plugin markers, the contributing plugin.
Related marker support¶
The same marker knowledge also drives go-to-declaration and rename for a marker name, a gutter icon on marker declarations, quick documentation for marker usages, and the Unknown pytest marker in strict mode inspection.
Notes¶
- Completion is offered only in real marker positions.
import pytest,import pytest as pt, andfrom pytest import markare recognised; unrelated decorators and a local object namedpytestare ignored. - The feature is gated by the Pytest mark completion setting.
Test Tree & Navigation¶
Pytest collection-pattern IDE integration¶
Collection-aware run actions¶
BetterPy keeps editor run gutters and Run/Debug context actions aligned with pytest's configured python_files, python_classes, python_functions, and testpaths. It adds run affordances for custom-named tests that pytest will collect and hides them where the configured rules exclude a file, class, or function, including inherited tests and explicit-path runs.
When renaming a collected test file, the rename dialog preselects the editable stem after the configured python_files prefix. For example, test_login.py selects login; a project configured with python_files = check_*.py selects login in check_login.py.
Copy pytest node IDs¶
Copy pytest node IDs copies pytest node identifiers for selected tests to the clipboard.
How to use it¶
Select one or more tests in the pytest test tree or pytest explorer, then run Copy Special | BetterPy: Copy Pytest Node IDs.
When it helps¶
Use this action when rerunning a specific test selection from a terminal, sharing an exact pytest target, or preparing command-line invocations.
Notes¶
- Multiple selected node IDs are copied one per line.
- The feature is gated by the Copy pytest node IDs setting under Settings | BetterPy | Testing (Pytest) | Test Tree & Navigation.
Copy pytest node ID from editor¶
Copy pytest node ID from editor copies the pytest node ID for the test at the caret.
How to use it¶
Place the caret inside a pytest test function, then run BetterPy: Copy Pytest Node ID from Copy Special.
When it helps¶
Use this action when you want to rerun the current test from a terminal or share an exact pytest target without opening the test tree.
Notes¶
- The action is available only inside pytest test functions.
- The feature is gated by the Copy pytest node ID from editor setting under Settings | BetterPy | Testing (Pytest) | Test Tree & Navigation.
Jump to pytest node in test tree¶
Jump to pytest node in test tree selects the matching node in the active pytest test tree for the test at the editor caret.
How to use it¶
Place the caret inside a pytest test function or class, then run BetterPy: Jump to Test Tree Node from the editor context menu or intention list.
The same entry is also offered in the run gutter menu of the test: click the run icon next to the test function or class and pick BetterPy: Jump to Test Tree Node.
When it helps¶
Use this feature after a pytest run when the editor is focused but you want to inspect the matching result node, failure state, or surrounding suite in the test tree.
Notes¶
- The action is available only when there is an active non-empty test tree.
- The feature no longer adds a separate gutter icon; it contributes an item to the existing run gutter menu.
- The feature is gated by the Jump to pytest node in test tree setting under Settings | BetterPy | Testing (Pytest) | Test Tree & Navigation.
Toggle pytest skip from test tree¶
Toggle pytest skip from test tree adds or removes @pytest.mark.skip for selected pytest nodes.
How to use it¶
Select a pytest function, class, module, or pytest explorer entry, then run BetterPy: Toggle Pytest Skip from the context menu.
When it helps¶
Use this action when triaging failures from the test results view and you want to skip or unskip the affected test without navigating manually through the project tree.
Notes¶
- Function selections toggle the marker on that function.
- Class and module selections toggle the marker at the matching scope.
- The feature is gated by the Toggle pytest skip from test tree setting under Settings | BetterPy | Testing (Pytest) | Test Tree & Navigation.
Run pytest with debug logging¶
Run pytest with debug logging duplicates the selected pytest run configuration, adds DEBUG CLI logging, and runs the selected pytest node.
How to use it¶
Select a pytest node in the test tree, then run BetterPy: Run Pytest with DEBUG logging from the test tree context menu.
When it helps¶
Use this action when a failing test needs extra pytest log output but you do not want to permanently change the original run configuration.
Notes¶
- The duplicated configuration is temporary.
- The duplicated configuration is narrowed to the selected test tree node (the pytest node id is targeted directly), so only that node runs.
- BetterPy appends
--log-cli-level=DEBUGwhen it is not already present. - The feature is gated by the Run pytest with debug logging setting under Settings | BetterPy | Testing (Pytest) | Test Tree & Navigation.
Pytest identifier in Search Everywhere¶
This Search Everywhere contributor finds pytest node identifiers directly from the IDE search popup.
How to use it¶
Open Search Everywhere and type a pytest node id or identifier fragment.
Notes¶
- Results navigate to the matching Python PSI element.
- The feature is gated by the Pytest identifier Search Everywhere setting.
Mocking¶
Mock assertion methods¶
Resolves standard Mock and AsyncMock assertion helpers such as assert_called_once_with without enabling broader mock type inference.
Mock patch reference¶
Provides reference resolution for mock.patch target strings, enabling ⌘+Click / Ctrl+Click navigation to the patched symbol.
Mock patch object attribute completion¶
Completes attributes for mock.patch.object targets.
Mock type provider¶
Provides type information for unittest.mock objects, improving code completion and type checking for mocked values.
Mock unresolved reference inspection¶
Reports unresolved references in mock usage