tests: enable flake8-pytest-style (PT) ruff rules#6857
Merged
Conversation
Enable the `PT` ruff rule set and fix the resulting violations across the test suite: - PT006: pass parametrize argument names as tuples instead of a single comma-separated string. - PT022: switch fixtures that have no teardown from `yield` to `return` so the lack of cleanup is obvious at a glance. - PT011: add `match=` to broad `pytest.raises(ValueError)` blocks so the expected error is anchored to a specific message. - PT012: hoist setup (patches, branching) out of `pytest.raises()` blocks so only the call that is expected to raise remains inside. - PT013: replace `from pytest import X` with `import pytest` and access attributes via the module. - PT015: replace `try/except` + `assert False` patterns with `pytest.raises(...)`. - PT017: replace `assert` on exceptions inside `except` blocks with `pytest.raises(...) as exc_info` and assert on `exc_info.value`. No behavioral changes to the tests; the full suite still passes.
Contributor
There was a problem hiding this comment.
Pull request overview
This PR enables Ruff’s PT (flake8-pytest-style) rules and updates the test suite to comply, primarily by tightening pytest idioms (parametrize arg naming, pytest.raises(...) usage) and simplifying fixtures that don’t need teardown.
Changes:
- Enable
PTrules in Ruff and apply mechanical fixes across the test suite. - Replace
yield-only fixtures (no teardown) withreturnand modernizepytestimports/usages. - Tighten exception assertions (e.g.,
pytest.raises(..., match=...)) and shrinkraisesblocks.
Reviewed changes
Copilot reviewed 77 out of 77 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| pyproject.toml | Enable Ruff PT rule set. |
| tests/conftest.py | Convert multiple fixtures from yield to return; adjust test helpers/clients. |
| tests/utils/test_exception_helper.py | Convert exception assertions to pytest.raises patterns; add helpers. |
| tests/utils/test_dbus.py | Return fixture value instead of yielding; parametrize argnames tuple. |
| tests/test_validate.py | Parametrize IPv6 DNS URL test; adjust raises block. |
| tests/test_updater.py | Parametrize argnames tuple. |
| tests/test_supervisor.py | Parametrize argnames tuple. |
| tests/test_auth.py | Use return fixtures (no teardown). |
| tests/store/test_validate.py | Parametrize argnames tuple. |
| tests/store/test_store_manager.py | Parametrize argnames tuple (multiple tests). |
| tests/store/test_repository_git.py | Hoist patching out of pytest.raises tuple-context pattern. |
| tests/store/test_custom_repository.py | Restructure async calls to keep only the raising await inside pytest.raises. |
| tests/resolution/evaluation/test_evaluate_os_version.py | Parametrize argnames tuple. |
| tests/resolution/evaluation/test_evaluate_home_assistant_core_version.py | Parametrize argnames tuple. |
| tests/resolution/check/test_check_network_interface_ipv4.py | Parametrize argnames tuple (multiple). |
| tests/resolution/check/test_check_multiple_data_disks.py | Use return fixture (no teardown). |
| tests/resolution/check/test_check_disabled_data_disk.py | Use return fixture (no teardown). |
| tests/plugins/test_plugin_base.py | Parametrize argnames tuple (multiple). |
| tests/plugins/test_dns.py | Replace try/except + assert-false with pytest.raises. |
| tests/mounts/test_mount.py | Parametrize argnames tuple; normalize param list container. |
| tests/mounts/test_manager.py | Use return fixture (no teardown). |
| tests/misc/test_tasks.py | Use return fixture (no teardown). |
| tests/jobs/test_job_manager.py | Add match= to pytest.raises(ValueError) assertions. |
| tests/jobs/test_job_decorator.py | Parametrize argnames tuple. |
| tests/host/test_network.py | Use return fixture (no teardown). |
| tests/host/test_manager.py | Use return fixture (no teardown). |
| tests/host/test_logs.py | Add match= to ValueError assertions; parametrize argnames tuple. |
| tests/host/test_firewall.py | Use return fixtures (no teardown). |
| tests/host/test_configuration.py | Replace manual try/except with pytest.raises(..., match=...); use return fixture. |
| tests/host/test_apparmor_control.py | Replace from pytest import raises with import pytest. |
| tests/homeassistant/test_core.py | Parametrize argnames tuple. |
| tests/hardware/test_helper.py | Replace from pytest import LogCaptureFixture with pytest.LogCaptureFixture. |
| tests/docker/test_monitor.py | Parametrize argnames tuple. |
| tests/docker/test_interface.py | Parametrize argnames tuple; reformat multi-arg parametrizations. |
| tests/dbus/udisks2/test_partition.py | Use return fixtures (no teardown). |
| tests/dbus/udisks2/test_partition_table.py | Use return fixtures (no teardown). |
| tests/dbus/udisks2/test_nvme_controller.py | Use return fixture (no teardown). |
| tests/dbus/udisks2/test_manager.py | Use return fixtures (no teardown). |
| tests/dbus/udisks2/test_filesystem.py | Use return fixtures (no teardown). |
| tests/dbus/udisks2/test_drive.py | Use return fixtures (no teardown). |
| tests/dbus/udisks2/test_block.py | Use return fixtures (no teardown). |
| tests/dbus/test_timedate.py | Use return fixture (no teardown). |
| tests/dbus/test_systemd.py | Use return fixture (no teardown). |
| tests/dbus/test_resolved.py | Use return fixture (no teardown). |
| tests/dbus/test_rauc.py | Use return fixture (no teardown). |
| tests/dbus/test_login.py | Use return fixture (no teardown). |
| tests/dbus/test_interface.py | Use return fixtures (no teardown). |
| tests/dbus/test_hostname.py | Use return fixture (no teardown). |
| tests/dbus/test_enum.py | Add match= to ValueError assertions. |
| tests/dbus/network/test_wireless.py | Use return fixture (no teardown). |
| tests/dbus/network/test_settings.py | Use return fixture (no teardown). |
| tests/dbus/network/test_network_manager.py | Use return fixture (no teardown). |
| tests/dbus/network/test_ip_configuration.py | Use return fixtures (no teardown). |
| tests/dbus/network/test_interface.py | Use return fixture (no teardown). |
| tests/dbus/network/test_dns.py | Use return fixture (no teardown). |
| tests/dbus/network/test_accesspoint.py | Use return fixture (no teardown). |
| tests/dbus/network/setting/test_init.py | Use return fixture; parametrize argnames tuple. |
| tests/dbus/agent/test_system.py | Use return fixture (no teardown). |
| tests/dbus/agent/test_swap.py | Use return fixture (no teardown). |
| tests/dbus/agent/test_datadisk.py | Use return fixture (no teardown). |
| tests/dbus/agent/test_cgroup.py | Use return fixture (no teardown). |
| tests/dbus/agent/test_apparmor.py | Use return fixture (no teardown). |
| tests/dbus/agent/test_agent.py | Use return fixture; parametrize argnames tuple. |
| tests/dbus/agent/boards/test_yellow.py | Use return fixture (no teardown). |
| tests/dbus/agent/boards/test_green.py | Use return fixture (no teardown). |
| tests/dbus/agent/boards/test_board.py | Use return fixture (no teardown). |
| tests/backups/test_manager.py | Parametrize argnames tuple (multiple). |
| tests/backups/test_backup.py | Adjust pytest.raises block contents. |
| tests/backups/conftest.py | Use return fixtures (no teardown). |
| tests/apps/test_manager.py | Use return fixture (no teardown). |
| tests/apps/test_app.py | Parametrize argnames tuple. |
| tests/api/test_os.py | Use return fixture (no teardown). |
| tests/api/test_mounts.py | Use return fixture (no teardown). |
| tests/api/test_host.py | Use return fixture (no teardown). |
| tests/api/test_backups.py | Parametrize argnames tuple (multiple). |
| tests/api/middleware/test_security.py | Use return fixtures (no teardown); parametrize argnames tuple. |
| tests/api/conftest.py | Use return fixture (no teardown). |
Comments suppressed due to low confidence (2)
tests/conftest.py:720
- This fixture now uses
return, but the signature still declaresGenerator[MagicMock]. Update the return type toMagicMock(or revert toyield) to keep the fixture typing consistent.
@pytest.fixture
def websession(coresys: CoreSys) -> Generator[MagicMock]:
"""Fixture for global aiohttp SessionClient.
Also mocks Core container is_running to return True so that
make_request doesn't bail before reaching the websession.
"""
coresys._websession = MagicMock(spec_set=ClientSession)
coresys.homeassistant.core.instance.is_running = AsyncMock(return_value=True)
return coresys._websession
tests/conftest.py:730
- This fixture now uses
return, but the signature still declaresGenerator[MockResponse]. Update the return type toMockResponse(or revert toyield) to keep the fixture typing consistent.
@pytest.fixture
def mock_update_data(websession: MagicMock) -> Generator[MockResponse]:
"""Mock updater JSON data."""
version_data = load_fixture("version_stable.json")
client_response = MockResponse(text=version_data)
client_response.status = 200
websession.get = MagicMock(return_value=client_response)
return client_response
- Fix fixture return-type annotations after switching `yield` to `return` in tests/conftest.py: drop the `Generator[...]`/`AsyncGenerator[...]` wrapper for `dns_manager_service`, `supervisor_internet`, `websession`, and `mock_update_data` so the annotation matches what the fixture actually returns. - Correct the return-type annotation of `fixture_ip6config_service` from `IP4ConfigService` to `IP6ConfigService`. - Fix recurring "excepiton" typo in tests/utils/test_exception_helper.py.
After `test_new_backup_permission_error` raises `BackupPermissionError`, assert that no tarfile was left behind and `tmp_path` is empty. The previous version only checked that the exception was raised, which missed any regression where a partial tarfile would survive the failed create.
The constant was named "good" but its tests assert that the URLs are rejected by the DNS validator. The IPv6 URLs are well-formed but currently rejected because IPv6 doesn't work with the Docker network (see `dns_url` in supervisor/validate.py). Rename the constant and the related test to make the intent obvious.
mdegat01
approved these changes
May 20, 2026
Contributor
mdegat01
left a comment
There was a problem hiding this comment.
Lots of small changes but yea I get the patterns, makes sense 👍
Contributor
Member
Author
Yeah quite some of these linter rules can be categorized as opinionated style. I tend to favor consistency, and enabling these rules do that. FWIW, Core uses the rule too. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Proposed change
Enable the
PT(flake8-pytest-style) rule set in ruff and fix the resulting violations across the test suite. The rules nudge tests toward idiomatic pytest usage and catch a few small footguns. No production code is touched and there are no behavioral changes to the tests themselves.Concrete categories addressed:
("a", "b")) instead of a single comma-separated string ("a,b").returninstead ofyield, so the absence of cleanup is obvious at a glance and the fixture isn't wrapped in unnecessary generator/finalizer machinery.pytest.raises(ValueError)blocks gained amatch=argument so the expected error is anchored to a specific message rather than catching anyValueError.pytest.raises()so only the call expected to raise remains inside the block.from pytest import Xreplaced withimport pytestand module-qualified access.try/except+assert Falsepatterns replaced withpytest.raises(...).exceptblocks replaced withpytest.raises(...) as exc_infoand assertions onexc_info.value.Most of the diff is mechanical (PT006 and PT022 together account for the vast majority of the changed lines). The full test suite passes.
Type of change
Additional information
Checklist
ruff format supervisor tests)If API endpoints or add-on configuration are added/changed: