Skip to content

Commit c3c5103

Browse files
committed
unittest: fix class instances no longer released on test teardown since pytest 8.2.0
Fix #12367.
1 parent 889d9b2 commit c3c5103

3 files changed

Lines changed: 23 additions & 17 deletions

File tree

changelog/12367.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix a regression in pytest 8.2.0 where unittest class instances (a fresh one is created for each test) were not released promptly on test teardown but only on session teardown.

src/_pytest/unittest.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,11 +218,12 @@ def setup(self) -> None:
218218
super().setup()
219219

220220
def teardown(self) -> None:
221-
super().teardown()
222221
if self._explicit_tearDown is not None:
223222
self._explicit_tearDown()
224223
self._explicit_tearDown = None
225224
self._obj = None
225+
self._instance = None
226+
super().teardown()
226227

227228
def startTest(self, testcase: "unittest.TestCase") -> None:
228229
pass

testing/test_unittest.py

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
# mypy: allow-untyped-defs
2-
import gc
32
import sys
43
from typing import List
54

@@ -192,30 +191,35 @@ def test_check(self):
192191
def test_teardown_issue1649(pytester: Pytester) -> None:
193192
"""
194193
Are TestCase objects cleaned up? Often unittest TestCase objects set
195-
attributes that are large and expensive during setUp.
194+
attributes that are large and expensive during test run or setUp.
196195
197196
The TestCase will not be cleaned up if the test fails, because it
198197
would then exist in the stackframe.
198+
199+
Regression test for #1649 (see also #12367).
199200
"""
200-
testpath = pytester.makepyfile(
201+
pytester.makepyfile(
201202
"""
202203
import unittest
203-
class TestCaseObjectsShouldBeCleanedUp(unittest.TestCase):
204-
def setUp(self):
205-
self.an_expensive_object = 1
206-
def test_demo(self):
207-
pass
204+
import gc
208205
209-
"""
206+
class TestCaseObjectsShouldBeCleanedUp(unittest.TestCase):
207+
def test_expensive(self):
208+
self.an_expensive_obj = object()
209+
210+
def test_is_it_still_alive(self):
211+
gc.collect()
212+
for obj in gc.get_objects():
213+
if type(obj).__name__ == "TestCaseObjectsShouldBeCleanedUp":
214+
assert not hasattr(obj, "an_expensive_obj")
215+
break
216+
else:
217+
assert False, "Could not find TestCaseObjectsShouldBeCleanedUp instance"
218+
"""
210219
)
211220

212-
pytester.inline_run("-s", testpath)
213-
gc.collect()
214-
215-
# Either already destroyed, or didn't run setUp.
216-
for obj in gc.get_objects():
217-
if type(obj).__name__ == "TestCaseObjectsShouldBeCleanedUp":
218-
assert not hasattr(obj, "an_expensive_obj")
221+
result = pytester.runpytest()
222+
assert result.ret == ExitCode.OK
219223

220224

221225
def test_unittest_skip_issue148(pytester: Pytester) -> None:

0 commit comments

Comments
 (0)