mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	Fixed #20550 -- Added ability to preserve test db between runs
This commit is contained in:
		@@ -82,8 +82,10 @@ class PostGISCreation(DatabaseCreation):
 | 
			
		||||
                self.connection.ops.quote_name(self.template_postgis),)
 | 
			
		||||
        return ''
 | 
			
		||||
 | 
			
		||||
    def _create_test_db(self, verbosity, autoclobber):
 | 
			
		||||
        test_database_name = super(PostGISCreation, self)._create_test_db(verbosity, autoclobber)
 | 
			
		||||
    def _create_test_db(self, verbosity, autoclobber, keepdb=False):
 | 
			
		||||
        test_database_name = super(PostGISCreation, self)._create_test_db(verbosity, autoclobber, keepdb)
 | 
			
		||||
        if keepdb:
 | 
			
		||||
            return test_database_name
 | 
			
		||||
        if self.template_postgis is None:
 | 
			
		||||
            # Connect to the test database in order to create the postgis extension
 | 
			
		||||
            self.connection.close()
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@ from django.db.backends.sqlite3.creation import DatabaseCreation
 | 
			
		||||
 | 
			
		||||
class SpatiaLiteCreation(DatabaseCreation):
 | 
			
		||||
 | 
			
		||||
    def create_test_db(self, verbosity=1, autoclobber=False):
 | 
			
		||||
    def create_test_db(self, verbosity=1, autoclobber=False, keepdb=False):
 | 
			
		||||
        """
 | 
			
		||||
        Creates a test database, prompting the user for confirmation if the
 | 
			
		||||
        database already exists. Returns the name of the test database created.
 | 
			
		||||
@@ -22,11 +22,15 @@ class SpatiaLiteCreation(DatabaseCreation):
 | 
			
		||||
 | 
			
		||||
        if verbosity >= 1:
 | 
			
		||||
            test_db_repr = ''
 | 
			
		||||
            action = 'Creating'
 | 
			
		||||
            if verbosity >= 2:
 | 
			
		||||
                test_db_repr = " ('%s')" % test_database_name
 | 
			
		||||
            print("Creating test database for alias '%s'%s..." % (self.connection.alias, test_db_repr))
 | 
			
		||||
            if keepdb:
 | 
			
		||||
                action = 'Using existing'
 | 
			
		||||
            print("%s test database for alias '%s'%s..." % (
 | 
			
		||||
                action, self.connection.alias, test_db_repr))
 | 
			
		||||
 | 
			
		||||
        self._create_test_db(verbosity, autoclobber)
 | 
			
		||||
        self._create_test_db(verbosity, autoclobber, keepdb)
 | 
			
		||||
 | 
			
		||||
        self.connection.close()
 | 
			
		||||
        self.connection.settings_dict["NAME"] = test_database_name
 | 
			
		||||
 
 | 
			
		||||
@@ -332,7 +332,7 @@ class BaseDatabaseCreation(object):
 | 
			
		||||
            ";",
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
    def create_test_db(self, verbosity=1, autoclobber=False):
 | 
			
		||||
    def create_test_db(self, verbosity=1, autoclobber=False, keepdb=False):
 | 
			
		||||
        """
 | 
			
		||||
        Creates a test database, prompting the user for confirmation if the
 | 
			
		||||
        database already exists. Returns the name of the test database created.
 | 
			
		||||
@@ -344,12 +344,21 @@ class BaseDatabaseCreation(object):
 | 
			
		||||
 | 
			
		||||
        if verbosity >= 1:
 | 
			
		||||
            test_db_repr = ''
 | 
			
		||||
            action = 'Creating'
 | 
			
		||||
            if verbosity >= 2:
 | 
			
		||||
                test_db_repr = " ('%s')" % test_database_name
 | 
			
		||||
            print("Creating test database for alias '%s'%s..." % (
 | 
			
		||||
                self.connection.alias, test_db_repr))
 | 
			
		||||
            if keepdb:
 | 
			
		||||
                action = "Using existing"
 | 
			
		||||
 | 
			
		||||
        self._create_test_db(verbosity, autoclobber)
 | 
			
		||||
            print("%s test database for alias '%s'%s..." % (
 | 
			
		||||
                action, self.connection.alias, test_db_repr))
 | 
			
		||||
 | 
			
		||||
        # We could skip this call if keepdb is True, but we instead
 | 
			
		||||
        # give it the keepdb param. This is to handle the case
 | 
			
		||||
        # where the test DB doesn't exist, in which case we need to
 | 
			
		||||
        # create it, then just not destroy it. If we instead skip
 | 
			
		||||
        # this, we will get an exception.
 | 
			
		||||
        self._create_test_db(verbosity, autoclobber, keepdb)
 | 
			
		||||
 | 
			
		||||
        self.connection.close()
 | 
			
		||||
        settings.DATABASES[self.connection.alias]["NAME"] = test_database_name
 | 
			
		||||
@@ -393,7 +402,7 @@ class BaseDatabaseCreation(object):
 | 
			
		||||
            return self.connection.settings_dict['TEST']['NAME']
 | 
			
		||||
        return TEST_DATABASE_PREFIX + self.connection.settings_dict['NAME']
 | 
			
		||||
 | 
			
		||||
    def _create_test_db(self, verbosity, autoclobber):
 | 
			
		||||
    def _create_test_db(self, verbosity, autoclobber, keepdb=False):
 | 
			
		||||
        """
 | 
			
		||||
        Internal implementation - creates the test db tables.
 | 
			
		||||
        """
 | 
			
		||||
@@ -409,6 +418,11 @@ class BaseDatabaseCreation(object):
 | 
			
		||||
                cursor.execute(
 | 
			
		||||
                    "CREATE DATABASE %s %s" % (qn(test_database_name), suffix))
 | 
			
		||||
            except Exception as e:
 | 
			
		||||
                # if we want to keep the db, then no need to do any of the below,
 | 
			
		||||
                # just return and skip it all.
 | 
			
		||||
                if keepdb:
 | 
			
		||||
                    return test_database_name
 | 
			
		||||
 | 
			
		||||
                sys.stderr.write(
 | 
			
		||||
                    "Got an error creating the test database: %s\n" % e)
 | 
			
		||||
                if not autoclobber:
 | 
			
		||||
 
 | 
			
		||||
@@ -56,7 +56,7 @@ class DatabaseCreation(BaseDatabaseCreation):
 | 
			
		||||
    def __init__(self, connection):
 | 
			
		||||
        super(DatabaseCreation, self).__init__(connection)
 | 
			
		||||
 | 
			
		||||
    def _create_test_db(self, verbosity=1, autoclobber=False):
 | 
			
		||||
    def _create_test_db(self, verbosity=1, autoclobber=False, keepdb=False):
 | 
			
		||||
        TEST_NAME = self._test_database_name()
 | 
			
		||||
        TEST_USER = self._test_database_user()
 | 
			
		||||
        TEST_PASSWD = self._test_database_passwd()
 | 
			
		||||
@@ -76,6 +76,10 @@ class DatabaseCreation(BaseDatabaseCreation):
 | 
			
		||||
            try:
 | 
			
		||||
                self._execute_test_db_creation(cursor, parameters, verbosity)
 | 
			
		||||
            except Exception as e:
 | 
			
		||||
                # if we want to keep the db, then no need to do any of the below,
 | 
			
		||||
                # just return and skip it all.
 | 
			
		||||
                if keepdb:
 | 
			
		||||
                    return
 | 
			
		||||
                sys.stderr.write("Got an error creating the test database: %s\n" % e)
 | 
			
		||||
                if not autoclobber:
 | 
			
		||||
                    confirm = input("It appears the test database, %s, already exists. Type 'yes' to delete it, or 'no' to cancel: " % TEST_NAME)
 | 
			
		||||
 
 | 
			
		||||
@@ -52,8 +52,10 @@ class DatabaseCreation(BaseDatabaseCreation):
 | 
			
		||||
            return test_database_name
 | 
			
		||||
        return ':memory:'
 | 
			
		||||
 | 
			
		||||
    def _create_test_db(self, verbosity, autoclobber):
 | 
			
		||||
    def _create_test_db(self, verbosity, autoclobber, keepdb=False):
 | 
			
		||||
        test_database_name = self._get_test_db_name()
 | 
			
		||||
        if keepdb:
 | 
			
		||||
            return test_database_name
 | 
			
		||||
        if test_database_name != ':memory:':
 | 
			
		||||
            # Erase the old test database
 | 
			
		||||
            if verbosity >= 1:
 | 
			
		||||
 
 | 
			
		||||
@@ -26,10 +26,13 @@ class DiscoverRunner(object):
 | 
			
		||||
        make_option('-p', '--pattern', action='store', dest='pattern',
 | 
			
		||||
            default="test*.py",
 | 
			
		||||
            help='The test matching pattern. Defaults to test*.py.'),
 | 
			
		||||
        make_option('-k', '--keepdb', action='store_true', dest='keepdb',
 | 
			
		||||
            default=False,
 | 
			
		||||
            help='Preserve the test DB between runs. Defaults to False'),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    def __init__(self, pattern=None, top_level=None,
 | 
			
		||||
                 verbosity=1, interactive=True, failfast=False,
 | 
			
		||||
                 verbosity=1, interactive=True, failfast=False, keepdb=False,
 | 
			
		||||
                 **kwargs):
 | 
			
		||||
 | 
			
		||||
        self.pattern = pattern
 | 
			
		||||
@@ -38,6 +41,7 @@ class DiscoverRunner(object):
 | 
			
		||||
        self.verbosity = verbosity
 | 
			
		||||
        self.interactive = interactive
 | 
			
		||||
        self.failfast = failfast
 | 
			
		||||
        self.keepdb = keepdb
 | 
			
		||||
 | 
			
		||||
    def setup_test_environment(self, **kwargs):
 | 
			
		||||
        setup_test_environment()
 | 
			
		||||
@@ -106,7 +110,7 @@ class DiscoverRunner(object):
 | 
			
		||||
        return reorder_suite(suite, self.reorder_by)
 | 
			
		||||
 | 
			
		||||
    def setup_databases(self, **kwargs):
 | 
			
		||||
        return setup_databases(self.verbosity, self.interactive, **kwargs)
 | 
			
		||||
        return setup_databases(self.verbosity, self.interactive, self.keepdb, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def run_suite(self, suite, **kwargs):
 | 
			
		||||
        return self.test_runner(
 | 
			
		||||
@@ -120,7 +124,7 @@ class DiscoverRunner(object):
 | 
			
		||||
        """
 | 
			
		||||
        old_names, mirrors = old_config
 | 
			
		||||
        for connection, old_name, destroy in old_names:
 | 
			
		||||
            if destroy:
 | 
			
		||||
            if destroy and not self.keepdb:
 | 
			
		||||
                connection.creation.destroy_test_db(old_name, self.verbosity)
 | 
			
		||||
 | 
			
		||||
    def teardown_test_environment(self, **kwargs):
 | 
			
		||||
@@ -250,7 +254,7 @@ def partition_suite(suite, classes, bins):
 | 
			
		||||
                bins[-1].addTest(test)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def setup_databases(verbosity, interactive, **kwargs):
 | 
			
		||||
def setup_databases(verbosity, interactive, keepdb=False, **kwargs):
 | 
			
		||||
    from django.db import connections, DEFAULT_DB_ALIAS
 | 
			
		||||
 | 
			
		||||
    # First pass -- work out which databases actually need to be created,
 | 
			
		||||
@@ -294,7 +298,7 @@ def setup_databases(verbosity, interactive, **kwargs):
 | 
			
		||||
            connection = connections[alias]
 | 
			
		||||
            if test_db_name is None:
 | 
			
		||||
                test_db_name = connection.creation.create_test_db(
 | 
			
		||||
                    verbosity, autoclobber=not interactive)
 | 
			
		||||
                    verbosity, autoclobber=not interactive, keepdb=keepdb)
 | 
			
		||||
                destroy = True
 | 
			
		||||
            else:
 | 
			
		||||
                connection.settings_dict['NAME'] = test_db_name
 | 
			
		||||
 
 | 
			
		||||
@@ -1310,6 +1310,17 @@ The ``--liveserver`` option can be used to override the default address where
 | 
			
		||||
the live server (used with :class:`~django.test.LiveServerTestCase`) is
 | 
			
		||||
expected to run from. The default value is ``localhost:8081``.
 | 
			
		||||
 | 
			
		||||
.. django-admin-option:: --keepdb
 | 
			
		||||
 | 
			
		||||
.. versionadded:: 1.8
 | 
			
		||||
 | 
			
		||||
The ``--keepdb`` option can be used to preserve the test database between test
 | 
			
		||||
runs. This has the advantage of skipping both the create and destroy actions
 | 
			
		||||
which greatly decreases the time to run tests, especially those in a large
 | 
			
		||||
test suite. If the test database does not exist, it will be created on the first
 | 
			
		||||
run and then preserved for each subsequent run. Any unapplied migrations will also
 | 
			
		||||
be applied to the test database before running the test suite.
 | 
			
		||||
 | 
			
		||||
testserver <fixture fixture ...>
 | 
			
		||||
--------------------------------
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -189,6 +189,9 @@ Tests
 | 
			
		||||
* The new :meth:`~django.test.SimpleTestCase.assertJSONNotEqual` assertion
 | 
			
		||||
  allows you to test that two JSON fragments are not equal.
 | 
			
		||||
 | 
			
		||||
* Added the ability to preserve the test database by adding the :djadminopt:`--keepdb`
 | 
			
		||||
  flag.
 | 
			
		||||
 | 
			
		||||
Validators
 | 
			
		||||
^^^^^^^^^^
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -149,6 +149,14 @@ Tests that require a database (namely, model tests) will not use your "real"
 | 
			
		||||
Regardless of whether the tests pass or fail, the test databases are destroyed
 | 
			
		||||
when all the tests have been executed.
 | 
			
		||||
 | 
			
		||||
.. versionadded:: 1.8
 | 
			
		||||
 | 
			
		||||
   You can prevent the test databases from being destroyed by adding the
 | 
			
		||||
   :djadminopt:`--keepdb` flag to the test command. This will preserve the test
 | 
			
		||||
   database between runs. If the database does not exist, it will first
 | 
			
		||||
   be created. Any migrations will also be applied in order to keep it
 | 
			
		||||
   up to date.
 | 
			
		||||
 | 
			
		||||
By default the test databases get their names by prepending ``test_``
 | 
			
		||||
to the value of the :setting:`NAME` settings for the databases
 | 
			
		||||
defined in :setting:`DATABASES`. When using the SQLite database engine
 | 
			
		||||
 
 | 
			
		||||
@@ -310,7 +310,7 @@ class AliasedDatabaseTeardownTest(unittest.TestCase):
 | 
			
		||||
        try:
 | 
			
		||||
            destroyed_names = []
 | 
			
		||||
            DatabaseCreation.destroy_test_db = lambda self, old_database_name, verbosity=1: destroyed_names.append(old_database_name)
 | 
			
		||||
            DatabaseCreation.create_test_db = lambda self, verbosity=1, autoclobber=False: self._get_test_db_name()
 | 
			
		||||
            DatabaseCreation.create_test_db = lambda self, verbosity=1, autoclobber=False, keepdb=False: self._get_test_db_name()
 | 
			
		||||
 | 
			
		||||
            db.connections = db.ConnectionHandler({
 | 
			
		||||
                'default': {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user