mirror of https://github.com/django/django.git
Fixed #20550 -- Added ability to preserve test db between runs
This commit is contained in:
parent
2e613ea5c5
commit
b7aa7c4ab4
|
@ -82,8 +82,10 @@ class PostGISCreation(DatabaseCreation):
|
||||||
self.connection.ops.quote_name(self.template_postgis),)
|
self.connection.ops.quote_name(self.template_postgis),)
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
def _create_test_db(self, verbosity, autoclobber):
|
def _create_test_db(self, verbosity, autoclobber, keepdb=False):
|
||||||
test_database_name = super(PostGISCreation, self)._create_test_db(verbosity, autoclobber)
|
test_database_name = super(PostGISCreation, self)._create_test_db(verbosity, autoclobber, keepdb)
|
||||||
|
if keepdb:
|
||||||
|
return test_database_name
|
||||||
if self.template_postgis is None:
|
if self.template_postgis is None:
|
||||||
# Connect to the test database in order to create the postgis extension
|
# Connect to the test database in order to create the postgis extension
|
||||||
self.connection.close()
|
self.connection.close()
|
||||||
|
|
|
@ -7,7 +7,7 @@ from django.db.backends.sqlite3.creation import DatabaseCreation
|
||||||
|
|
||||||
class SpatiaLiteCreation(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
|
Creates a test database, prompting the user for confirmation if the
|
||||||
database already exists. Returns the name of the test database created.
|
database already exists. Returns the name of the test database created.
|
||||||
|
@ -22,11 +22,15 @@ class SpatiaLiteCreation(DatabaseCreation):
|
||||||
|
|
||||||
if verbosity >= 1:
|
if verbosity >= 1:
|
||||||
test_db_repr = ''
|
test_db_repr = ''
|
||||||
|
action = 'Creating'
|
||||||
if verbosity >= 2:
|
if verbosity >= 2:
|
||||||
test_db_repr = " ('%s')" % test_database_name
|
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.close()
|
||||||
self.connection.settings_dict["NAME"] = test_database_name
|
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
|
Creates a test database, prompting the user for confirmation if the
|
||||||
database already exists. Returns the name of the test database created.
|
database already exists. Returns the name of the test database created.
|
||||||
|
@ -344,12 +344,21 @@ class BaseDatabaseCreation(object):
|
||||||
|
|
||||||
if verbosity >= 1:
|
if verbosity >= 1:
|
||||||
test_db_repr = ''
|
test_db_repr = ''
|
||||||
|
action = 'Creating'
|
||||||
if verbosity >= 2:
|
if verbosity >= 2:
|
||||||
test_db_repr = " ('%s')" % test_database_name
|
test_db_repr = " ('%s')" % test_database_name
|
||||||
print("Creating test database for alias '%s'%s..." % (
|
if keepdb:
|
||||||
self.connection.alias, test_db_repr))
|
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()
|
self.connection.close()
|
||||||
settings.DATABASES[self.connection.alias]["NAME"] = test_database_name
|
settings.DATABASES[self.connection.alias]["NAME"] = test_database_name
|
||||||
|
@ -393,7 +402,7 @@ class BaseDatabaseCreation(object):
|
||||||
return self.connection.settings_dict['TEST']['NAME']
|
return self.connection.settings_dict['TEST']['NAME']
|
||||||
return TEST_DATABASE_PREFIX + self.connection.settings_dict['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.
|
Internal implementation - creates the test db tables.
|
||||||
"""
|
"""
|
||||||
|
@ -409,6 +418,11 @@ class BaseDatabaseCreation(object):
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
"CREATE DATABASE %s %s" % (qn(test_database_name), suffix))
|
"CREATE DATABASE %s %s" % (qn(test_database_name), suffix))
|
||||||
except Exception as e:
|
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(
|
sys.stderr.write(
|
||||||
"Got an error creating the test database: %s\n" % e)
|
"Got an error creating the test database: %s\n" % e)
|
||||||
if not autoclobber:
|
if not autoclobber:
|
||||||
|
|
|
@ -56,7 +56,7 @@ class DatabaseCreation(BaseDatabaseCreation):
|
||||||
def __init__(self, connection):
|
def __init__(self, connection):
|
||||||
super(DatabaseCreation, self).__init__(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_NAME = self._test_database_name()
|
||||||
TEST_USER = self._test_database_user()
|
TEST_USER = self._test_database_user()
|
||||||
TEST_PASSWD = self._test_database_passwd()
|
TEST_PASSWD = self._test_database_passwd()
|
||||||
|
@ -76,6 +76,10 @@ class DatabaseCreation(BaseDatabaseCreation):
|
||||||
try:
|
try:
|
||||||
self._execute_test_db_creation(cursor, parameters, verbosity)
|
self._execute_test_db_creation(cursor, parameters, verbosity)
|
||||||
except Exception as e:
|
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)
|
sys.stderr.write("Got an error creating the test database: %s\n" % e)
|
||||||
if not autoclobber:
|
if not autoclobber:
|
||||||
confirm = input("It appears the test database, %s, already exists. Type 'yes' to delete it, or 'no' to cancel: " % TEST_NAME)
|
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 test_database_name
|
||||||
return ':memory:'
|
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()
|
test_database_name = self._get_test_db_name()
|
||||||
|
if keepdb:
|
||||||
|
return test_database_name
|
||||||
if test_database_name != ':memory:':
|
if test_database_name != ':memory:':
|
||||||
# Erase the old test database
|
# Erase the old test database
|
||||||
if verbosity >= 1:
|
if verbosity >= 1:
|
||||||
|
|
|
@ -26,10 +26,13 @@ class DiscoverRunner(object):
|
||||||
make_option('-p', '--pattern', action='store', dest='pattern',
|
make_option('-p', '--pattern', action='store', dest='pattern',
|
||||||
default="test*.py",
|
default="test*.py",
|
||||||
help='The test matching pattern. Defaults to 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,
|
def __init__(self, pattern=None, top_level=None,
|
||||||
verbosity=1, interactive=True, failfast=False,
|
verbosity=1, interactive=True, failfast=False, keepdb=False,
|
||||||
**kwargs):
|
**kwargs):
|
||||||
|
|
||||||
self.pattern = pattern
|
self.pattern = pattern
|
||||||
|
@ -38,6 +41,7 @@ class DiscoverRunner(object):
|
||||||
self.verbosity = verbosity
|
self.verbosity = verbosity
|
||||||
self.interactive = interactive
|
self.interactive = interactive
|
||||||
self.failfast = failfast
|
self.failfast = failfast
|
||||||
|
self.keepdb = keepdb
|
||||||
|
|
||||||
def setup_test_environment(self, **kwargs):
|
def setup_test_environment(self, **kwargs):
|
||||||
setup_test_environment()
|
setup_test_environment()
|
||||||
|
@ -106,7 +110,7 @@ class DiscoverRunner(object):
|
||||||
return reorder_suite(suite, self.reorder_by)
|
return reorder_suite(suite, self.reorder_by)
|
||||||
|
|
||||||
def setup_databases(self, **kwargs):
|
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):
|
def run_suite(self, suite, **kwargs):
|
||||||
return self.test_runner(
|
return self.test_runner(
|
||||||
|
@ -120,7 +124,7 @@ class DiscoverRunner(object):
|
||||||
"""
|
"""
|
||||||
old_names, mirrors = old_config
|
old_names, mirrors = old_config
|
||||||
for connection, old_name, destroy in old_names:
|
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)
|
connection.creation.destroy_test_db(old_name, self.verbosity)
|
||||||
|
|
||||||
def teardown_test_environment(self, **kwargs):
|
def teardown_test_environment(self, **kwargs):
|
||||||
|
@ -250,7 +254,7 @@ def partition_suite(suite, classes, bins):
|
||||||
bins[-1].addTest(test)
|
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
|
from django.db import connections, DEFAULT_DB_ALIAS
|
||||||
|
|
||||||
# First pass -- work out which databases actually need to be created,
|
# First pass -- work out which databases actually need to be created,
|
||||||
|
@ -294,7 +298,7 @@ def setup_databases(verbosity, interactive, **kwargs):
|
||||||
connection = connections[alias]
|
connection = connections[alias]
|
||||||
if test_db_name is None:
|
if test_db_name is None:
|
||||||
test_db_name = connection.creation.create_test_db(
|
test_db_name = connection.creation.create_test_db(
|
||||||
verbosity, autoclobber=not interactive)
|
verbosity, autoclobber=not interactive, keepdb=keepdb)
|
||||||
destroy = True
|
destroy = True
|
||||||
else:
|
else:
|
||||||
connection.settings_dict['NAME'] = test_db_name
|
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
|
the live server (used with :class:`~django.test.LiveServerTestCase`) is
|
||||||
expected to run from. The default value is ``localhost:8081``.
|
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 ...>
|
testserver <fixture fixture ...>
|
||||||
--------------------------------
|
--------------------------------
|
||||||
|
|
||||||
|
|
|
@ -189,6 +189,9 @@ Tests
|
||||||
* The new :meth:`~django.test.SimpleTestCase.assertJSONNotEqual` assertion
|
* The new :meth:`~django.test.SimpleTestCase.assertJSONNotEqual` assertion
|
||||||
allows you to test that two JSON fragments are not equal.
|
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
|
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
|
Regardless of whether the tests pass or fail, the test databases are destroyed
|
||||||
when all the tests have been executed.
|
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_``
|
By default the test databases get their names by prepending ``test_``
|
||||||
to the value of the :setting:`NAME` settings for the databases
|
to the value of the :setting:`NAME` settings for the databases
|
||||||
defined in :setting:`DATABASES`. When using the SQLite database engine
|
defined in :setting:`DATABASES`. When using the SQLite database engine
|
||||||
|
|
|
@ -310,7 +310,7 @@ class AliasedDatabaseTeardownTest(unittest.TestCase):
|
||||||
try:
|
try:
|
||||||
destroyed_names = []
|
destroyed_names = []
|
||||||
DatabaseCreation.destroy_test_db = lambda self, old_database_name, verbosity=1: destroyed_names.append(old_database_name)
|
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({
|
db.connections = db.ConnectionHandler({
|
||||||
'default': {
|
'default': {
|
||||||
|
|
Loading…
Reference in New Issue