summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--django/contrib/gis/db/backends/postgis/creation.py6
-rw-r--r--django/contrib/gis/db/backends/spatialite/creation.py10
-rw-r--r--django/db/backends/creation.py24
-rw-r--r--django/db/backends/oracle/creation.py6
-rw-r--r--django/db/backends/sqlite3/creation.py4
-rw-r--r--django/test/runner.py14
-rw-r--r--docs/ref/django-admin.txt11
-rw-r--r--docs/releases/1.8.txt3
-rw-r--r--docs/topics/testing/overview.txt8
-rw-r--r--tests/test_runner/tests.py2
10 files changed, 70 insertions, 18 deletions
diff --git a/django/contrib/gis/db/backends/postgis/creation.py b/django/contrib/gis/db/backends/postgis/creation.py
index 82be18cb65..8c9a27b8c5 100644
--- a/django/contrib/gis/db/backends/postgis/creation.py
+++ b/django/contrib/gis/db/backends/postgis/creation.py
@@ -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()
diff --git a/django/contrib/gis/db/backends/spatialite/creation.py b/django/contrib/gis/db/backends/spatialite/creation.py
index 8cadef9f9c..205e601ba3 100644
--- a/django/contrib/gis/db/backends/spatialite/creation.py
+++ b/django/contrib/gis/db/backends/spatialite/creation.py
@@ -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
diff --git a/django/db/backends/creation.py b/django/db/backends/creation.py
index b2b82c13c2..72fea241dc 100644
--- a/django/db/backends/creation.py
+++ b/django/db/backends/creation.py
@@ -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"
+
+ print("%s test database for alias '%s'%s..." % (
+ action, self.connection.alias, test_db_repr))
- self._create_test_db(verbosity, autoclobber)
+ # 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:
diff --git a/django/db/backends/oracle/creation.py b/django/db/backends/oracle/creation.py
index 90cc83fd58..d93aac4d77 100644
--- a/django/db/backends/oracle/creation.py
+++ b/django/db/backends/oracle/creation.py
@@ -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)
diff --git a/django/db/backends/sqlite3/creation.py b/django/db/backends/sqlite3/creation.py
index abcd068952..2519add07c 100644
--- a/django/db/backends/sqlite3/creation.py
+++ b/django/db/backends/sqlite3/creation.py
@@ -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:
diff --git a/django/test/runner.py b/django/test/runner.py
index 5eafe35416..68671e6525 100644
--- a/django/test/runner.py
+++ b/django/test/runner.py
@@ -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
diff --git a/docs/ref/django-admin.txt b/docs/ref/django-admin.txt
index 6bb67f01fc..9291479305 100644
--- a/docs/ref/django-admin.txt
+++ b/docs/ref/django-admin.txt
@@ -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 ...>
--------------------------------
diff --git a/docs/releases/1.8.txt b/docs/releases/1.8.txt
index b0e3b035c0..3c5a44fc44 100644
--- a/docs/releases/1.8.txt
+++ b/docs/releases/1.8.txt
@@ -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
^^^^^^^^^^
diff --git a/docs/topics/testing/overview.txt b/docs/topics/testing/overview.txt
index 10dbc70f39..2d9808f0bc 100644
--- a/docs/topics/testing/overview.txt
+++ b/docs/topics/testing/overview.txt
@@ -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
diff --git a/tests/test_runner/tests.py b/tests/test_runner/tests.py
index 8ec244cf8d..6de53b8283 100644
--- a/tests/test_runner/tests.py
+++ b/tests/test_runner/tests.py
@@ -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': {