unittest: inline `_make_xunit_fixture`

The indirection makes things harder to follow in this case IMO.
This commit is contained in:
Ran Benita 2024-01-05 13:18:10 +02:00
parent c2a4a8d518
commit a616adf3ae
1 changed files with 57 additions and 81 deletions

View File

@ -29,7 +29,6 @@ from _pytest.python import Class
from _pytest.python import Function from _pytest.python import Function
from _pytest.python import Module from _pytest.python import Module
from _pytest.runner import CallInfo from _pytest.runner import CallInfo
from _pytest.scope import Scope
if TYPE_CHECKING: if TYPE_CHECKING:
import unittest import unittest
@ -71,7 +70,8 @@ class UnitTestCase(Class):
skipped = _is_skipped(cls) skipped = _is_skipped(cls)
if not skipped: if not skipped:
self._inject_setup_teardown_fixtures(cls) self._inject_unittest_setup_method_fixture(cls)
self._inject_unittest_setup_class_fixture(cls)
self._inject_setup_class_fixture() self._inject_setup_class_fixture()
self.session._fixturemanager.parsefactories(self, unittest=True) self.session._fixturemanager.parsefactories(self, unittest=True)
@ -93,91 +93,67 @@ class UnitTestCase(Class):
if ut is None or runtest != ut.TestCase.runTest: # type: ignore if ut is None or runtest != ut.TestCase.runTest: # type: ignore
yield TestCaseFunction.from_parent(self, name="runTest") yield TestCaseFunction.from_parent(self, name="runTest")
def _inject_setup_teardown_fixtures(self, cls: type) -> None: def _inject_unittest_setup_class_fixture(self, cls: type) -> None:
"""Injects a hidden auto-use fixture to invoke setUpClass/setup_method and corresponding """Injects a hidden auto-use fixture to invoke setUpClass and
teardown functions (#517).""" tearDownClass (#517)."""
class_fixture = _make_xunit_fixture( setup = getattr(cls, "setUpClass", None)
cls, teardown = getattr(cls, "tearDownClass", None)
"setUpClass", if setup is None and teardown is None:
"tearDownClass", return None
"doClassCleanups", cleanup = getattr(cls, "doClassCleanups", lambda: None)
scope=Scope.Class,
pass_self=False, @pytest.fixture(
scope="class",
autouse=True,
# Use a unique name to speed up lookup.
name=f"_unittest_setUpClass_fixture_{cls.__qualname__}",
) )
if class_fixture: def fixture(self) -> Generator[None, None, None]:
cls.__pytest_class_setup = class_fixture # type: ignore[attr-defined] if _is_skipped(self):
reason = self.__unittest_skip_why__
method_fixture = _make_xunit_fixture( raise pytest.skip.Exception(reason, _use_item_location=True)
cls, if setup is not None:
"setup_method", try:
"teardown_method",
None,
scope=Scope.Function,
pass_self=True,
)
if method_fixture:
cls.__pytest_method_setup = method_fixture # type: ignore[attr-defined]
def _make_xunit_fixture(
obj: type,
setup_name: str,
teardown_name: str,
cleanup_name: Optional[str],
scope: Scope,
pass_self: bool,
):
setup = getattr(obj, setup_name, None)
teardown = getattr(obj, teardown_name, None)
if setup is None and teardown is None:
return None
if cleanup_name:
cleanup = getattr(obj, cleanup_name, lambda *args: None)
else:
def cleanup(*args):
pass
@pytest.fixture(
scope=scope.value,
autouse=True,
# Use a unique name to speed up lookup.
name=f"_unittest_{setup_name}_fixture_{obj.__qualname__}",
)
def fixture(self, request: FixtureRequest) -> Generator[None, None, None]:
if _is_skipped(self):
reason = self.__unittest_skip_why__
raise pytest.skip.Exception(reason, _use_item_location=True)
if setup is not None:
try:
if pass_self:
setup(self, request.function)
else:
setup() setup()
# unittest does not call the cleanup function for every BaseException, so we # unittest does not call the cleanup function for every BaseException, so we
# follow this here. # follow this here.
except Exception: except Exception:
if pass_self:
cleanup(self)
else:
cleanup() cleanup()
raise
raise yield
yield try:
try: if teardown is not None:
if teardown is not None:
if pass_self:
teardown(self, request.function)
else:
teardown() teardown()
finally: finally:
if pass_self:
cleanup(self)
else:
cleanup() cleanup()
return fixture cls.__pytest_class_setup = fixture # type: ignore[attr-defined]
def _inject_unittest_setup_method_fixture(self, cls: type) -> None:
"""Injects a hidden auto-use fixture to invoke setup_method and
teardown_method (#517)."""
setup = getattr(cls, "setup_method", None)
teardown = getattr(cls, "teardown_method", None)
if setup is None and teardown is None:
return None
@pytest.fixture(
scope="function",
autouse=True,
# Use a unique name to speed up lookup.
name=f"_unittest_setup_method_fixture_{cls.__qualname__}",
)
def fixture(self, request: FixtureRequest) -> Generator[None, None, None]:
if _is_skipped(self):
reason = self.__unittest_skip_why__
raise pytest.skip.Exception(reason, _use_item_location=True)
if setup is not None:
setup(self, request.function)
yield
if teardown is not None:
teardown(self, request.function)
cls.__pytest_method_setup = fixture # type: ignore[attr-defined]
class TestCaseFunction(Function): class TestCaseFunction(Function):