Skip to content

Testing (Pytest)

BetterPy's largest feature group — 29 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.

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.

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.

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.

@pytest.fixture
def client(app):
    return app.test_client()

def test_health(cli<caret>):
    ...

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

Since 2026.07.0 · Maturity Incubating

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.

def test_migrates_database(db):
    assert run_migration()
becomes:
@pytest.mark.usefixtures("db")
def test_migrates_database():
    assert run_migration()

@pytest.mark.usefixtures("db")
def test_migrates_database():
    assert run_migration()
becomes:
def test_migrates_database(db):
    assert run_migration()

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.

Pytest fixture documentation

Since 2026.05.2 · Maturity Stable · Issues PY-66245

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, and float.
  • Generator-style fixtures annotated as Generator[T, ...], Iterator[T], Iterable[T], and their async variants; BetterPy documents and infers the yielded value type T.
  • 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 request fixture, which is typed as _pytest.fixtures.SubRequest when 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

Since 2026.07.0 · Maturity Incubating

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.
  • Fixture generation imports pytest when 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

Since 2026.07.0 · Maturity Incubating

Adds a Generate menu action for pytest hook stubs in conftest.py and go-to-declaration for hook functions.

How to invoke: ⌘N / Alt+Insert in conftest.py"BetterPy: Pytest Hook"


Fixture Inspections

Pytest fixture scope inspection

Since 2026.07.0 · Maturity Incubating

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.

Unused pytest fixture inspection

Since 2026.07.0 · Maturity Incubating

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

Since 2026.07.0 · Maturity Incubating

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_plugins when 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

Since 2026.07.0 · Maturity Incubating

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

Since 2026.07.0 · Maturity Incubating

Reports pytest fixture overrides whose produced value type is incompatible with the parent fixture they shadow.

Pytest redundant fixture import inspection

Since 2026.07.0 · Maturity Incubating

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)
# test_something.py

def test_example(my_fixture):
    assert my_fixture == 42

request.param without params inspection

Since 2026.07.0 · Maturity Incubating

Reports fixtures that read request.param without declaring params= and without being targeted by indirect parametrization.

Unknown dynamic pytest fixture inspection

Since 2026.07.0 · Maturity Incubating

Reports unresolved string-based fixture requests in usefixtures, getfixturevalue, and indirect parametrization.

Pytest fixture uninjected reference inspection

Since 2026.07.0 · Maturity Incubating · Issues PY-71966PY-89003

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

Since 2026.07.0 · Maturity Incubating

@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

Since 2026.07.0 · Maturity Incubating

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.

Parametrize

Parametrize pytest test

Since 2026.07.0 · Maturity Incubating · Issues PY-73660

Parametrize pytest test adds, removes, 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.

When it helps

Use these intentions when turning a single test into a parametrized test, adding another parameter to an existing decorator, 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.
  • The feature is gated by the Parametrize pytest test setting under Settings | BetterPy | Testing (Pytest) | Parametrize.

Pytest parametrized case run gutter icon

Since 2026.06.0 · Maturity Incubating · Issues PY-88558PY-46650

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.

import pytest

@pytest.mark.parametrize("value", [
    "admin",
    "guest",
])
def test_role(value):
    assert value
test_roles.py::test_role[guest]

Notes

  • The line marker appears only when BetterPy can infer the exact pytest case ID.
  • Explicit ids from ids=[...] and pytest.param(..., id="...") are supported.
  • The feature is gated by the Pytest parametrized case run gutter icon setting under Settings | BetterPy | Testing (Pytest) | Parametrize.

Convert pytest.param

Since 2026.07.0 · Maturity Incubating

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 pytest when converting plain values to pytest.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.

Assertions & Test Editing

Use actual test outcome

Since 2026.06.0 · Maturity Incubating

Replaces assertion expected values with the actual value from the last failed test run.

How to invoke: ⌥⏎ / Alt+Enter on a failed assertion → "BetterPy: Use actual test outcome"

def test_total():
    assert calculate_total([10, 20]) == 25  # Failed: got 30
def test_total():
    assert calculate_total([10, 20]) == 30

Toggle pytest skip

Since 2026.07.0 · Maturity Incubating

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()

Since 2026.07.0 · Maturity Incubating

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 pytest import 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

Since 2026.06.0 · Maturity Incubating · Issues PY-32337PY-61067

Marks the line where a pytest test failed, providing visual feedback directly in the editor.

def test_total():
    total = calculate_total([10, 20])
    assert total == 25
def test_total():
    total = calculate_total([10, 20])
    assert total == 25  # highlighted when pytest reports this line as failed

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.

Test Tree & Navigation

Copy pytest node IDs

Since 2026.07.0 · Maturity Incubating

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

Since 2026.07.0 · Maturity Incubating

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

Since 2026.07.0 · Maturity Incubating

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.

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 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

Since 2026.07.0 · Maturity Incubating

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

Since 2026.07.0 · Maturity Incubating

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=DEBUG when 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

Since 2026.07.0 · Maturity Incubating

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.

tests/test_api.py::TestUser::test_create

Notes

  • Results navigate to the matching Python PSI element.
  • The feature is gated by the Pytest identifier Search Everywhere setting.