Repository: django/django
File: django/db/backends/base/creation.py (Python)

Code before:
        # to do so, because it's not allowed to delete a database while being
        # connected to it.
        with self._nodb_cursor() as cursor:
            cursor.execute(
                "DROP DATABASE %s" % self.connection.ops.quote_name(test_database_name)
            )

    def mark_expected_failures_and_skips(self):
        """
        Mark tests in Django's test suite which are expected failures on this
        database and test which should be skipped on this database.
        """
        # Only load unittest if we're actually testing.
        from unittest import expectedFailure, skip

        def get_test(module_or_class, class_or_method, test_name):
            try:
                test_case = import_string(module_or_class)
            except ImportError:
                # If import_string couldn't chop the rightmost term
                # and getattr() it from the remainder, it's likely that
                # the rightmost term was a submodule, and the remainder
                # is a module that hasn't been imported yet.
                # Import the test_method by importing test_name.
                test_method = import_string(test_name)
                test_case = sys.modules.get(test_method.__module__)
            else:
                test_method = getattr(test_case, class_or_method)
            return test_case, test_method

        for test_name in self.connection.features.django_test_expected_failures:
            module_or_class_name, _, class_or_method_name = test_name.rpartition(".")
            test_app = test_name.split(".")[0]
            # Importing a test app that isn't installed raises RuntimeError.
            if test_app in settings.INSTALLED_APPS:
                test_case, test_method = get_test(
                    module_or_class_name, class_or_method_name, test_name
                )
                setattr(test_case, class_or_method_name, expectedFailure(test_method))
        for reason, tests in self.connection.features.django_test_skips.items():
            for test_name in tests:
                module_or_class_name, _, class_or_method_name = test_name.rpartition(
                    "."
                )
                test_app = test_name.split(".")[0]
                # Importing a test app that isn't installed raises
                # RuntimeError.
                if test_app in settings.INSTALLED_APPS:
                    test_case, test_method = get_test(
                        module_or_class_name, class_or_method_name, test_name
                    )

Review comment (bug):
I think this is actually unnecessary. If the import fails, that means the test isn't going to run, so decorating it isn't needed. In other words:
```diff
diff --git a/django/db/backends/base/creation.py b/django/db/backends/base/creation.py
index 3ba9365a2c..34eae215a4 100644
--- a/django/db/backends/base/creation.py
+++ b/django/db/backends/base/creation.py
@@ -364,8 +364,8 @@ class BaseDatabaseCreation:
                 # the rightmost term was a submodule, and the remainder
                 # is a module that hasn't been imported yet.
                 # Import the test_method by importing test_name.
-                test_method = import_string(test_name)
-                test_case = sys.modules.get(test_method.__module__)
+                import_string(test_name)
+                return (None, None)
             else:
                 test_method = getattr(test_case, class_or_method)
             return test_case, test_method
@@ -378,7 +378,8 @@ class BaseDatabaseCreation:
                 test_case, test_method = get_test(
                     module_or_class_name, class_or_method_name, test_name
                 )
-                setattr(test_case, class_or_method_name, expectedFailure(test_method))
+                if test_case:
+                    setattr(test_case, class_or_method_name, expectedFailure(test_method))
         for reason, tests in self.connection.features.django_test_skips.items():
             for test_name in tests:
                 module_or_class_name, _, class_or_method_name = test_name.rpartition(
@@ -391,7 +392,8 @@ class BaseDatabaseCreation:
                     test_case, test_method = get_test(
                         module_or_class_name, class_or_method_name, test_name
                     )
-                    setattr(test_case, class_or_method_name, skip(reason)(test_method))
+                    if test_case:
+                        setattr(test_case, class_or_method_name, skip(reason)(test_method))
 
     def sql_table_creation_suffix(self):
```



As I said in my other comment, the only reason the second import is useful is to catch typos in test names. 


Code after:
        # to do so, because it's not allowed to delete a database while being
        # connected to it.
        with self._nodb_cursor() as cursor:
            cursor.execute(
                "DROP DATABASE %s" % self.connection.ops.quote_name(test_database_name)
            )

    def mark_expected_failures_and_skips(self):
        """
        Mark tests in Django's test suite which are expected failures on this
        database and test which should be skipped on this database.
        """
        for test_name in self.connection.features.django_test_expected_failures:
            self._mark_test(test_name)
        for reason, tests in self.connection.features.django_test_skips.items():
            for test_name in tests:
                self._mark_test(test_name, reason)

    def _mark_test(self, test_name, skip_reason=None):
        # Only load unittest during testing.
        from unittest import expectedFailure, skip

        module_or_class_name, _, name_to_mark = test_name.rpartition(".")
        test_app = test_name.split(".")[0]
        # Importing a test app that isn't installed raises RuntimeError.
        if test_app in settings.INSTALLED_APPS:
            try:
                test_frame = import_string(module_or_class_name)
            except ImportError:
                # import_string() can raise ImportError if a submodule's parent
                # module hasn't already been imported during test discovery.
                # This can happen in at least two cases:
                # 1. When running a subset of tests in a module, the test
                #    runner won't import tests in that module's other
                #    submodules.
                # 2. When the parallel test runner spawns workers with an empty
                #    import cache.
                test_to_mark = import_string(test_name)
                test_frame = sys.modules.get(test_to_mark.__module__)
            else:
                test_to_mark = getattr(test_frame, name_to_mark)
            if skip_reason:
                setattr(test_frame, name_to_mark, skip(skip_reason)(test_to_mark))
            else:
                setattr(test_frame, name_to_mark, expectedFailure(test_to_mark))

    def sql_table_creation_suffix(self):
        """
        SQL to append to the end of the test table creation statements.
        """
        return ""