]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.11] gh-84867: Do not load tests from TestCase and FunctionTestCase (GH-100497...
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Tue, 12 Sep 2023 14:05:58 +0000 (07:05 -0700)
committerGitHub <noreply@github.com>
Tue, 12 Sep 2023 14:05:58 +0000 (14:05 +0000)
(cherry picked from commit 66d1d7eb067d445f1ade151f4a6db3864dd9109f)

Co-authored-by: Nikita Sobolev <mail@sobolevn.me>
Lib/unittest/loader.py
Lib/unittest/test/test_loader.py
Misc/NEWS.d/next/Library/2022-12-24-12-50-54.gh-issue-84867.OhaLbU.rst [new file with mode: 0644]

index 7e6ce2f224bafc0dc08ba70019c37918ee094be5..f4e3d6e8f275dc5d21fa08753e43cba63c35cb9c 100644 (file)
@@ -87,9 +87,13 @@ class TestLoader(object):
             raise TypeError("Test cases should not be derived from "
                             "TestSuite. Maybe you meant to derive from "
                             "TestCase?")
-        testCaseNames = self.getTestCaseNames(testCaseClass)
-        if not testCaseNames and hasattr(testCaseClass, 'runTest'):
-            testCaseNames = ['runTest']
+        if testCaseClass in (case.TestCase, case.FunctionTestCase):
+            # We don't load any tests from base types that should not be loaded.
+            testCaseNames = []
+        else:
+            testCaseNames = self.getTestCaseNames(testCaseClass)
+            if not testCaseNames and hasattr(testCaseClass, 'runTest'):
+                testCaseNames = ['runTest']
         loaded_suite = self.suiteClass(map(testCaseClass, testCaseNames))
         return loaded_suite
 
@@ -120,7 +124,11 @@ class TestLoader(object):
         tests = []
         for name in dir(module):
             obj = getattr(module, name)
-            if isinstance(obj, type) and issubclass(obj, case.TestCase):
+            if (
+                isinstance(obj, type)
+                and issubclass(obj, case.TestCase)
+                and obj not in (case.TestCase, case.FunctionTestCase)
+            ):
                 tests.append(self.loadTestsFromTestCase(obj))
 
         load_tests = getattr(module, 'load_tests', None)
@@ -189,7 +197,11 @@ class TestLoader(object):
 
         if isinstance(obj, types.ModuleType):
             return self.loadTestsFromModule(obj)
-        elif isinstance(obj, type) and issubclass(obj, case.TestCase):
+        elif (
+            isinstance(obj, type)
+            and issubclass(obj, case.TestCase)
+            and obj not in (case.TestCase, case.FunctionTestCase)
+        ):
             return self.loadTestsFromTestCase(obj)
         elif (isinstance(obj, types.FunctionType) and
               isinstance(parent, type) and
index de2268cda906882377602d23e121f90714b3649a..24001822b2f13654aa0a5b245da90e7cdb08477d 100644 (file)
@@ -102,6 +102,22 @@ class Test_TestLoader(unittest.TestCase):
         self.assertIsInstance(suite, loader.suiteClass)
         self.assertEqual(list(suite), [Foo('runTest')])
 
+    # "Do not load any tests from `TestCase` class itself."
+    def test_loadTestsFromTestCase__from_TestCase(self):
+        loader = unittest.TestLoader()
+
+        suite = loader.loadTestsFromTestCase(unittest.TestCase)
+        self.assertIsInstance(suite, loader.suiteClass)
+        self.assertEqual(list(suite), [])
+
+    # "Do not load any tests from `FunctionTestCase` class."
+    def test_loadTestsFromTestCase__from_FunctionTestCase(self):
+        loader = unittest.TestLoader()
+
+        suite = loader.loadTestsFromTestCase(unittest.FunctionTestCase)
+        self.assertIsInstance(suite, loader.suiteClass)
+        self.assertEqual(list(suite), [])
+
     ################################################################
     ### /Tests for TestLoader.loadTestsFromTestCase
 
@@ -123,6 +139,19 @@ class Test_TestLoader(unittest.TestCase):
         expected = [loader.suiteClass([MyTestCase('test')])]
         self.assertEqual(list(suite), expected)
 
+    # "This test ensures that internal `TestCase` subclasses are not loaded"
+    def test_loadTestsFromModule__TestCase_subclass_internals(self):
+        # See https://github.com/python/cpython/issues/84867
+        m = types.ModuleType('m')
+        # Simulate imported names:
+        m.TestCase = unittest.TestCase
+        m.FunctionTestCase = unittest.FunctionTestCase
+
+        loader = unittest.TestLoader()
+        suite = loader.loadTestsFromModule(m)
+        self.assertIsInstance(suite, loader.suiteClass)
+        self.assertEqual(list(suite), [])
+
     # "This method searches `module` for classes derived from TestCase"
     #
     # What happens if no tests are found (no TestCase instances)?
diff --git a/Misc/NEWS.d/next/Library/2022-12-24-12-50-54.gh-issue-84867.OhaLbU.rst b/Misc/NEWS.d/next/Library/2022-12-24-12-50-54.gh-issue-84867.OhaLbU.rst
new file mode 100644 (file)
index 0000000..8b45dce
--- /dev/null
@@ -0,0 +1,2 @@
+:class:`unittest.TestLoader` no longer loads test cases from exact
+:class:`unittest.TestCase` and :class:`unittest.FunctionTestCase` classes.