Issue #22903: The fake test case created by unittest.loader when it fails importing...
authorAntoine Pitrou <solipsis@pitrou.net>
Wed, 18 Mar 2015 22:56:46 +0000 (23:56 +0100)
committerAntoine Pitrou <solipsis@pitrou.net>
Wed, 18 Mar 2015 22:56:46 +0000 (23:56 +0100)
Lib/unittest/loader.py
Lib/unittest/test/test_discovery.py
Misc/NEWS

index 808c50eb668193dd90830899c0118a24a1c22c80..af39216d2638b0ed907b470de6376cf8557af5c2 100644 (file)
@@ -19,20 +19,31 @@ __unittest = True
 VALID_MODULE_NAME = re.compile(r'[_a-z]\w*\.py$', re.IGNORECASE)
 
 
+class _FailedTest(case.TestCase):
+    _testMethodName = None
+
+    def __init__(self, method_name, exception):
+        self._exception = exception
+        super(_FailedTest, self).__init__(method_name)
+
+    def __getattr__(self, name):
+        if name != self._testMethodName:
+            return super(_FailedTest, self).__getattr__(name)
+        def testFailure():
+            raise self._exception
+        return testFailure
+
+
 def _make_failed_import_test(name, suiteClass):
     message = 'Failed to import test module: %s\n%s' % (name, traceback.format_exc())
-    return _make_failed_test('ModuleImportFailure', name, ImportError(message),
-                             suiteClass)
+    return _make_failed_test(name, ImportError(message), suiteClass)
 
 def _make_failed_load_tests(name, exception, suiteClass):
-    return _make_failed_test('LoadTestsFailure', name, exception, suiteClass)
+    return _make_failed_test(name, exception, suiteClass)
 
-def _make_failed_test(classname, methodname, exception, suiteClass):
-    def testFailure(self):
-        raise exception
-    attrs = {methodname: testFailure}
-    TestClass = type(classname, (case.TestCase,), attrs)
-    return suiteClass((TestClass(methodname),))
+def _make_failed_test(methodname, exception, suiteClass):
+    test = _FailedTest(methodname, exception)
+    return suiteClass((test,))
 
 def _make_skipped_test(methodname, exception, suiteClass):
     @case.skip(str(exception))
index abb90669b3044911986918b24d8ac3eed9f8cbee..f12e8983cdb129ec1c1a0a565b42befb853de177 100644 (file)
@@ -2,6 +2,7 @@ import os
 import re
 import sys
 import types
+import pickle
 import builtins
 from test import support
 
@@ -216,6 +217,10 @@ class TestDiscovery(unittest.TestCase):
         with self.assertRaises(ImportError):
             test.test_this_does_not_exist()
 
+        # Check picklability
+        for proto in range(pickle.HIGHEST_PROTOCOL + 1):
+            pickle.loads(pickle.dumps(test, proto))
+
     def test_discover_with_module_that_raises_SkipTest_on_import(self):
         loader = unittest.TestLoader()
 
@@ -232,6 +237,10 @@ class TestDiscovery(unittest.TestCase):
         suite.run(result)
         self.assertEqual(len(result.skipped), 1)
 
+        # Check picklability
+        for proto in range(pickle.HIGHEST_PROTOCOL + 1):
+            pickle.loads(pickle.dumps(suite, proto))
+
     def test_command_line_handling_parseArgs(self):
         program = TestableTestProgram()
 
index 90edc969562bb57052e3f10c1dbeeed20daaad7d..2ec41cd0c623ebadd51ecb48748d5d7be0d01c6e 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -18,6 +18,9 @@ Core and Builtins
 Library
 -------
 
+- Issue #22903: The fake test case created by unittest.loader when it fails
+  importing a test module is now picklable.
+
 - Issue #23568: Add rdivmod support to MagicMock() objects.
   Patch by Håkan Lövdahl.