Skip to content

Commit d00ca3f

Browse files
committed
unittest: restore UnitTestFunction.obj to return unbound rather than bound method
This fixes pytest-dev#9610. pytest 7.0.0 (unintentionally) changed `UnitTestFunction.obj`'s' behavior to match `Function.obj`. That is probably a good thing to have, however it evidently causes some regressions as described in the issue, so restore the previous behavior for now. In the future we might want to make this change again, but with proper consideration.
1 parent bc33ba0 commit d00ca3f

File tree

3 files changed

+38
-0
lines changed

3 files changed

+38
-0
lines changed

changelog/9610.bugfix.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Restore `UnitTestFunction.obj` to return unbound rather than bound method.
2+
Fixes a crash during a failed teardown in unittest TestCases with non-default `__init__`.
3+
Regressed in pytest 7.0.0.

src/_pytest/unittest.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,15 @@ class TestCaseFunction(Function):
185185
_excinfo: Optional[List[_pytest._code.ExceptionInfo[BaseException]]] = None
186186
_testcase: Optional["unittest.TestCase"] = None
187187

188+
def _getobj(self):
189+
assert self.parent is not None
190+
# Unlike a regular Function in a Class, where `item.obj` returns
191+
# a *bound* method (attached to an instance), TestCaseFunction's
192+
# `obj` returns an *unbound* method (not attached to an instance).
193+
# This inconsistency is probably not desirable, but needs some
194+
# consideration before changing.
195+
return getattr(self.parent.obj, self.originalname) # type: ignore[attr-defined]
196+
188197
def setup(self) -> None:
189198
# A bound method to be called during teardown() if set (see 'runtest()').
190199
self._explicit_tearDown: Optional[Callable[[], None]] = None

testing/test_unittest.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1472,3 +1472,29 @@ def test_cleanup_called_the_right_number_of_times():
14721472
passed, skipped, failed = reprec.countoutcomes()
14731473
assert failed == 2
14741474
assert passed == 1
1475+
1476+
1477+
def test_traceback_pruning(pytester: Pytester) -> None:
1478+
"""Regression test for #9610 - doesn't crash during traceback pruning."""
1479+
pytester.makepyfile(
1480+
"""
1481+
import unittest
1482+
1483+
class MyTestCase(unittest.TestCase):
1484+
def __init__(self, test_method):
1485+
unittest.TestCase.__init__(self, test_method)
1486+
1487+
class TestIt(MyTestCase):
1488+
@classmethod
1489+
def tearDownClass(cls) -> None:
1490+
assert False
1491+
1492+
def test_it(self):
1493+
pass
1494+
"""
1495+
)
1496+
reprec = pytester.inline_run()
1497+
passed, skipped, failed = reprec.countoutcomes()
1498+
assert passed == 1
1499+
assert failed == 1
1500+
assert reprec.ret == 1

0 commit comments

Comments
 (0)