Issue #22903: The fake test case created by unittest.loader when it fails importing a test module is now picklable.
diff --git a/Lib/unittest/loader.py b/Lib/unittest/loader.py
index 8ee6c56..8a1a2a7 100644
--- a/Lib/unittest/loader.py
+++ b/Lib/unittest/loader.py
@@ -20,23 +20,34 @@
 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, message)
+    return _make_failed_test(name, ImportError(message), suiteClass, message)
 
 def _make_failed_load_tests(name, exception, suiteClass):
     message = 'Failed to call load_tests:\n%s' % (traceback.format_exc(),)
     return _make_failed_test(
-        'LoadTestsFailure', name, exception, suiteClass, message)
+        name, exception, suiteClass, message)
 
-def _make_failed_test(classname, methodname, exception, suiteClass, message):
-    def testFailure(self):
-        raise exception
-    attrs = {methodname: testFailure}
-    TestClass = type(classname, (case.TestCase,), attrs)
-    return suiteClass((TestClass(methodname),)), message
+def _make_failed_test(methodname, exception, suiteClass, message):
+    test = _FailedTest(methodname, exception)
+    return suiteClass((test,)), message
 
 def _make_skipped_test(methodname, exception, suiteClass):
     @case.skip(str(exception))
@@ -169,7 +180,7 @@
                 else:
                     # Otherwise, we signal that an AttributeError has occurred.
                     error_case, error_message = _make_failed_test(
-                        'AttributeError', part, e, self.suiteClass,
+                        part, e, self.suiteClass,
                         'Failed to access attribute:\n%s' % (
                             traceback.format_exc(),))
                     self.errors.append(error_message)
diff --git a/Lib/unittest/test/test_discovery.py b/Lib/unittest/test/test_discovery.py
index 4f61314..8991f38 100644
--- a/Lib/unittest/test/test_discovery.py
+++ b/Lib/unittest/test/test_discovery.py
@@ -3,6 +3,7 @@
 import re
 import sys
 import types
+import pickle
 import builtins
 from test import support
 
@@ -482,6 +483,10 @@
             test.my_package()
         self.assertEqual(import_calls, ['my_package'])
 
+        # 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()
 
@@ -498,6 +503,10 @@
         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_discover_with_init_module_that_raises_SkipTest_on_import(self):
         vfs = {abspath('/foo'): ['my_package'],
                abspath('/foo/my_package'): ['__init__.py', 'test_module.py']}
@@ -518,6 +527,10 @@
         self.assertEqual(result.testsRun, 1)
         self.assertEqual(import_calls, ['my_package'])
 
+        # 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()
 
diff --git a/Misc/NEWS b/Misc/NEWS
index de3f381..ccc2945 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -18,6 +18,9 @@
 Library
 -------
 
+- Issue #22903: The fake test case created by unittest.loader when it fails
+  importing a test module is now picklable.
+
 - Issue #22181: On Linux, os.urandom() now uses the new getrandom() syscall if
   available, syscall introduced in the Linux kernel 3.17. It is more reliable
   and more secure, because it avoids the need of a file descriptor and waits