From ca4dc624c693ed09b71bca8bd95de33f28163b58 Mon Sep 17 00:00:00 2001 From: Friday Date: Wed, 18 Feb 2026 09:25:58 +0000 Subject: [PATCH 1/2] Fix autouse fixtures running in @unittest.skip classes (#13885) When a unittest.TestCase is decorated with @unittest.skipIf(True, ...) or @unittest.skip, autouse pytest fixtures defined inside the class were still being executed. This is a regression since pytest 8.1.0 (commit c8792bd) which changed fixture registration to skip registering the unittest setup fixtures when the class is skipped, but this also meant no skip fixture was registered to preempt user-defined autouse fixtures. Fix by registering a dedicated skip fixture for classes marked with @unittest.skip, ensuring the skip happens before any other fixtures run. Co-Authored-By: Claude Opus 4.6 --- changelog/13885.bugfix.rst | 1 + src/_pytest/unittest.py | 18 ++++++++++++++++++ testing/test_unittest.py | 21 +++++++++++++++++++++ 3 files changed, 40 insertions(+) create mode 100644 changelog/13885.bugfix.rst diff --git a/changelog/13885.bugfix.rst b/changelog/13885.bugfix.rst new file mode 100644 index 00000000000..c4b7ebe4b4e --- /dev/null +++ b/changelog/13885.bugfix.rst @@ -0,0 +1 @@ +Fixed autouse fixtures defined inside a :class:`unittest.TestCase` class running even when the class is decorated with :func:`unittest.skip` or :func:`unittest.skipIf` -- regression since pytest 8.1.0. diff --git a/src/_pytest/unittest.py b/src/_pytest/unittest.py index 31be8847821..c9dbfda1a20 100644 --- a/src/_pytest/unittest.py +++ b/src/_pytest/unittest.py @@ -100,6 +100,8 @@ def collect(self) -> Iterable[Item | Collector]: self._register_unittest_setup_method_fixture(cls) self._register_unittest_setup_class_fixture(cls) self._register_setup_class_fixture() + else: + self._register_unittest_skip_fixture(cls) self.session._fixturemanager.parsefactories(self.newinstance(), self.nodeid) @@ -175,6 +177,22 @@ def unittest_setup_class_fixture( autouse=True, ) + def _register_unittest_skip_fixture(self, cls: type) -> None: + """Register an auto-use fixture to skip tests for a class decorated + with @unittest.skip or @unittest.skipIf (#13885).""" + + def unittest_skip_fixture(request: FixtureRequest) -> None: + reason = cls.__unittest_skip_why__ + raise skip.Exception(reason, _use_item_location=True) + + self.session._fixturemanager._register_fixture( + name=f"_unittest_skip_fixture_{cls.__qualname__}", + func=unittest_skip_fixture, + nodeid=self.nodeid, + scope="class", + autouse=True, + ) + def _register_unittest_setup_method_fixture(self, cls: type) -> None: """Register an auto-use fixture to invoke setup_method and teardown_method (#517).""" diff --git a/testing/test_unittest.py b/testing/test_unittest.py index 395c9fe647e..5d74d514c3c 100644 --- a/testing/test_unittest.py +++ b/testing/test_unittest.py @@ -244,6 +244,27 @@ def tearDownClass(self): reprec.assertoutcome(skipped=1) +def test_unittest_skip_with_autouse_fixture(pytester: Pytester) -> None: + """Autouse fixtures inside a @unittest.skipIf class should not run (#13885).""" + pytester.makepyfile( + """ + import unittest + import pytest + + @unittest.skipIf(True, "skip reason") + class TestSkipped(unittest.TestCase): + @pytest.fixture(autouse=True) + def my_fixture(self): + raise RuntimeError("fixture should not run") + + def test_one(self): + pass + """ + ) + reprec = pytester.inline_run() + reprec.assertoutcome(skipped=1) + + def test_method_and_teardown_failing_reporting(pytester: Pytester) -> None: pytester.makepyfile( """ From 036828d164eadc6730228b9514ce2e0752bcda81 Mon Sep 17 00:00:00 2001 From: Friday Date: Wed, 18 Feb 2026 13:09:38 +0000 Subject: [PATCH 2/2] Fix mypy attr-defined error for __unittest_skip_why__ Use getattr() instead of direct attribute access to satisfy mypy's type checking, since __unittest_skip_why__ is set dynamically by unittest.skip decorators and not visible to the type checker. Co-Authored-By: Claude Opus 4.6 --- src/_pytest/unittest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_pytest/unittest.py b/src/_pytest/unittest.py index c9dbfda1a20..9de620f9254 100644 --- a/src/_pytest/unittest.py +++ b/src/_pytest/unittest.py @@ -182,7 +182,7 @@ def _register_unittest_skip_fixture(self, cls: type) -> None: with @unittest.skip or @unittest.skipIf (#13885).""" def unittest_skip_fixture(request: FixtureRequest) -> None: - reason = cls.__unittest_skip_why__ + reason = getattr(cls, "__unittest_skip_why__", "") raise skip.Exception(reason, _use_item_location=True) self.session._fixturemanager._register_fixture(