Fixed #12542 -- Added the TEST_MIRROR setting, allowing testing of read slave databases.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@12289 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Russell Keith-Magee 2010-01-25 12:05:38 +00:00
parent 739d5aec0f
commit 6755a039eb
4 changed files with 99 additions and 11 deletions

View File

@ -65,6 +65,7 @@ class ConnectionHandler(object):
conn.setdefault('TEST_CHARSET', None) conn.setdefault('TEST_CHARSET', None)
conn.setdefault('TEST_COLLATION', None) conn.setdefault('TEST_COLLATION', None)
conn.setdefault('TEST_NAME', None) conn.setdefault('TEST_NAME', None)
conn.setdefault('TEST_MIRROR', None)
conn.setdefault('TIME_ZONE', settings.TIME_ZONE) conn.setdefault('TIME_ZONE', settings.TIME_ZONE)
for setting in ('NAME', 'USER', 'PASSWORD', 'HOST', 'PORT'): for setting in ('NAME', 'USER', 'PASSWORD', 'HOST', 'PORT'):
conn.setdefault(setting, '') conn.setdefault(setting, '')

View File

@ -231,16 +231,30 @@ class DjangoTestSuiteRunner(object):
def setup_databases(self): def setup_databases(self):
from django.db import connections from django.db import connections
old_names = [] old_names = []
mirrors = []
for alias in connections: for alias in connections:
connection = connections[alias] connection = connections[alias]
old_names.append((connection, connection.settings_dict['NAME'])) # If the database is a test mirror, redirect it's connection
connection.creation.create_test_db(self.verbosity, autoclobber=not self.interactive) # instead of creating a test database.
return old_names if connection.settings_dict['TEST_MIRROR']:
mirrors.append((alias, connection))
mirror_alias = connection.settings_dict['TEST_MIRROR']
connections._connections[alias] = connections[mirror_alias]
else:
old_names.append((connection, connection.settings_dict['NAME']))
connection.creation.create_test_db(self.verbosity, autoclobber=not self.interactive)
return old_names, mirrors
def run_suite(self, suite): def run_suite(self, suite):
return DjangoTestRunner(verbosity=self.verbosity, failfast=self.failfast).run(suite) return DjangoTestRunner(verbosity=self.verbosity, failfast=self.failfast).run(suite)
def teardown_databases(self, old_names): def teardown_databases(self, old_config):
from django.db import connections
old_names, mirrors = old_config
# Point all the mirrors back to the originals
for alias, connection in mirrors:
connections._connections[alias] = connection
# Destroy all the non-mirror databases
for connection, old_name in old_names: for connection, old_name in old_names:
connection.creation.destroy_test_db(old_name, self.verbosity) connection.creation.destroy_test_db(old_name, self.verbosity)
@ -273,11 +287,11 @@ class DjangoTestSuiteRunner(object):
suite = self.build_suite(test_labels, extra_tests) suite = self.build_suite(test_labels, extra_tests)
old_names = self.setup_databases() old_config = self.setup_databases()
result = self.run_suite(suite) result = self.run_suite(suite)
self.teardown_databases(old_names) self.teardown_databases(old_config)
self.teardown_test_environment() self.teardown_test_environment()

View File

@ -357,6 +357,21 @@ Only supported for the ``mysql`` backend (see the `MySQL manual`_ for details).
.. _MySQL manual: MySQL_ .. _MySQL manual: MySQL_
.. setting:: TEST_MIRROR
TEST_MIRROR
~~~~~~~~~~~
Default: ``None``
The alias of the database that this database should mirror during
testing.
This setting exists to allow for testing of master/slave
configurations of multiple databases. See the documentation on
:ref:`testing master/slave configurations
<topics-testing-masterslave>` for details.
.. setting:: TEST_NAME .. setting:: TEST_NAME
TEST_NAME TEST_NAME

View File

@ -301,12 +301,12 @@ 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.
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 databased 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
the tests will by default use an in-memory database (i.e., the the tests will by default use an in-memory database (i.e., the
database will be created in memory, bypassing the filesystem database will be created in memory, bypassing the filesystem
entirely!). If you want to use a different database name, specify entirely!). If you want to use a different database name, specify
``TEST_NAME`` in the dictionary for any given database in :setting:`TEST_NAME` in the dictionary for any given database in
:setting:`DATABASES`. :setting:`DATABASES`.
Aside from using a separate database, the test runner will otherwise Aside from using a separate database, the test runner will otherwise
@ -325,6 +325,58 @@ control the particular collation used by the test database. See the
:ref:`settings documentation <ref-settings>` for details of these :ref:`settings documentation <ref-settings>` for details of these
advanced settings. advanced settings.
.. _topics-testing-masterslave:
Testing master/slave configurations
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. versionadded:: 1.2
If you're testing a multiple database configuration with master/slave
replication, this strategy of creating test databases poses a problem.
When the test databases are created, there won't be any replication,
and as a result, data created on the master won't be seen on the
slave.
To compensate for this, Django allows you to define that a database is
a *test mirror*. Consider the following (simplified) example database
configuration::
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'myproject',
'HOST': 'dbmaster',
# ... plus some other settings
},
'slave': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'myproject',
'HOST': 'dbslave',
'TEST_MIRROR': 'default'
# ... plus some other settings
}
}
In this setup, we have two database servers: ``dbmaster``, described
by the database alias ``default``, and ``dbslave`` described by the
alias ``slave``. As you might expect, ``dbslave`` has been configured
by the database administrator as a read slave of ``dbmaster``, so in
normal activity, any write to ``default`` will appear on ``slave``.
If Django created two independent test databases, this would break any
tests that expected replication to occur. However, the ``slave``
database has been configured as a test mirror (using the
:setting:`TEST_MIRROR` setting), indicating that under testing,
``slave`` should be treated as a mirror of ``default``.
When the test environment is configured, a test version of ``slave``
will *not* be created. Instead the connection to ``slave``
will be redirected to point at ``default``. As a result, writes to
``default`` will appear on ``slave`` -- but because they are actually
the same database, not because there is data replication between the
two databases.
Other test conditions Other test conditions
--------------------- ---------------------
@ -1349,7 +1401,9 @@ set up, execute and tear down the test suite.
Creates the test databases. Creates the test databases.
Returns the list of old database names that will need to be restored Returns a data structure that provides enough detail to undo the changes
that have been made. This data will be provided to the ``teardown_databases()``
function at the conclusion of testing.
.. method:: DjangoTestSuiteRunner.run_suite(suite) .. method:: DjangoTestSuiteRunner.run_suite(suite)
@ -1357,9 +1411,13 @@ set up, execute and tear down the test suite.
Returns the result produced by the running the test suite. Returns the result produced by the running the test suite.
.. method:: DjangoTestSuiteRunner.teardown_databases(old_names) .. method:: DjangoTestSuiteRunner.teardown_databases(old_config)
Destroys the test databases, restoring the old names. Destroys the test databases, restoring pre-test conditions.
``old_config`` is a data structure defining the changes in the
database configuration that need to be reversed. It is the return
value of the ``setup_databases()`` method.
.. method:: DjangoTestSuiteRunner.teardown_test_environment() .. method:: DjangoTestSuiteRunner.teardown_test_environment()