From c144e5f6d2c2b06fa523d4e0fabdf1080f3f3eaf Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 21 Nov 2022 13:49:30 +0200 Subject: [PATCH 1/2] gh-99645: Fix a bug in handling class cleanups in unittest.TestCase Now addClassCleanup() uses separate lists for different TestCase subclasses, and doClassCleanups() only cleans up the particular class. --- Lib/test/test_unittest/test_runner.py | 38 +++++++++++++++++++ Lib/unittest/case.py | 10 ++--- ...2-11-21-13-49-03.gh-issue-99645.9w1QKq.rst | 3 ++ 3 files changed, 46 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2022-11-21-13-49-03.gh-issue-99645.9w1QKq.rst diff --git a/Lib/test/test_unittest/test_runner.py b/Lib/test/test_unittest/test_runner.py index d396f2bab57871..dd0932755b10fd 100644 --- a/Lib/test/test_unittest/test_runner.py +++ b/Lib/test/test_unittest/test_runner.py @@ -536,6 +536,44 @@ def testNothing(self): self.assertEqual(TestableTest._class_cleanups, []) + def test_run_nested_test(self): + ordering = [] + + class InnerTest(unittest.TestCase): + @classmethod + def setUpClass(cls): + ordering.append('setUpClass2') + cls.addClassCleanup(ordering.append, 'cleanup2') + def test(self): + ordering.append('test2') + + class OuterTest(unittest.TestCase): + @classmethod + def setUpClass(cls): + ordering.append('setUpClass1') + cls.addClassCleanup(ordering.append, 'cleanup1') + def test(self): + ordering.append('start test1') + runTests(InnerTest) + ordering.append('end test1') + + runTests(OuterTest) + self.assertEqual(ordering, ['setUpClass1', 'start test1', + 'setUpClass2', 'test2', 'cleanup2', + 'end test1', 'cleanup1']) + + + def test_debug_nested_test(self): + ordering = [] + + class InnerTest(unittest.TestCase): + @classmethod + def setUpClass(cls): + ordering.append('setUpClass2') + cls.addClassCleanup(ordering.append, 'cleanup2') + def test(self): + ordering.append('test2') + class TestModuleCleanUp(unittest.TestCase): def test_add_and_do_ModuleCleanup(self): diff --git a/Lib/unittest/case.py b/Lib/unittest/case.py index b01f6605e23e39..5167c5f843f085 100644 --- a/Lib/unittest/case.py +++ b/Lib/unittest/case.py @@ -384,11 +384,11 @@ class TestCase(object): # of difflib. See #11763. _diffThreshold = 2**16 - # Attribute used by TestSuite for classSetUp - - _classSetupFailed = False - - _class_cleanups = [] + def __init_subclass__(cls, *args, **kwargs): + # Attribute used by TestSuite for classSetUp + cls._classSetupFailed = False + cls._class_cleanups = [] + super().__init_subclass__(*args, **kwargs) def __init__(self, methodName='runTest'): """Create an instance of the class that will use the named test diff --git a/Misc/NEWS.d/next/Library/2022-11-21-13-49-03.gh-issue-99645.9w1QKq.rst b/Misc/NEWS.d/next/Library/2022-11-21-13-49-03.gh-issue-99645.9w1QKq.rst new file mode 100644 index 00000000000000..f6ee449891d9f6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-11-21-13-49-03.gh-issue-99645.9w1QKq.rst @@ -0,0 +1,3 @@ +Fix a bug in handling class cleanups in :class:`unittest.TestCase`. Now +``addClassCleanup()`` uses separate lists for different ``TestCase`` +subclasses, and ``doClassCleanups()`` only cleans up the particular class. From 7819ed691102028f2dfbf09a8d8d54c57101e8eb Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 22 Nov 2022 16:29:51 +0200 Subject: [PATCH 2/2] Address review comments. --- Lib/test/test_unittest/test_runner.py | 33 +++++++++------------------ 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/Lib/test/test_unittest/test_runner.py b/Lib/test/test_unittest/test_runner.py index dd0932755b10fd..8e95e9de69010c 100644 --- a/Lib/test/test_unittest/test_runner.py +++ b/Lib/test/test_unittest/test_runner.py @@ -542,37 +542,26 @@ def test_run_nested_test(self): class InnerTest(unittest.TestCase): @classmethod def setUpClass(cls): - ordering.append('setUpClass2') - cls.addClassCleanup(ordering.append, 'cleanup2') + ordering.append('inner setup') + cls.addClassCleanup(ordering.append, 'inner cleanup') def test(self): - ordering.append('test2') + ordering.append('inner test') class OuterTest(unittest.TestCase): @classmethod def setUpClass(cls): - ordering.append('setUpClass1') - cls.addClassCleanup(ordering.append, 'cleanup1') + ordering.append('outer setup') + cls.addClassCleanup(ordering.append, 'outer cleanup') def test(self): - ordering.append('start test1') + ordering.append('start outer test') runTests(InnerTest) - ordering.append('end test1') + ordering.append('end outer test') runTests(OuterTest) - self.assertEqual(ordering, ['setUpClass1', 'start test1', - 'setUpClass2', 'test2', 'cleanup2', - 'end test1', 'cleanup1']) - - - def test_debug_nested_test(self): - ordering = [] - - class InnerTest(unittest.TestCase): - @classmethod - def setUpClass(cls): - ordering.append('setUpClass2') - cls.addClassCleanup(ordering.append, 'cleanup2') - def test(self): - ordering.append('test2') + self.assertEqual(ordering, [ + 'outer setup', 'start outer test', + 'inner setup', 'inner test', 'inner cleanup', + 'end outer test', 'outer cleanup']) class TestModuleCleanUp(unittest.TestCase):