Skip to content

Commit 6684110

Browse files
github-actions[bot]bluetech
andauthoredFeb 9, 2022
[7.0.x] unittest: restore UnitTestFunction.obj to return unbound rather than bound method (#9656)
Co-authored-by: Ran Benita <[email protected]>
1 parent ed8255c commit 6684110

File tree

3 files changed

+38
-0
lines changed

3 files changed

+38
-0
lines changed
 

‎changelog/9610.bugfix.rst

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

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

+26
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)
Please sign in to comment.