Author: russellm Date: 2010-01-25 06:05:38 -0600 (Mon, 25 Jan 2010) New Revision: 12289
Modified: django/trunk/django/db/utils.py django/trunk/django/test/simple.py django/trunk/docs/ref/settings.txt django/trunk/docs/topics/testing.txt Log: Fixed #12542 -- Added the TEST_MIRROR setting, allowing testing of read slave databases. Modified: django/trunk/django/db/utils.py =================================================================== --- django/trunk/django/db/utils.py 2010-01-24 00:10:30 UTC (rev 12288) +++ django/trunk/django/db/utils.py 2010-01-25 12:05:38 UTC (rev 12289) @@ -65,6 +65,7 @@ conn.setdefault('TEST_CHARSET', None) conn.setdefault('TEST_COLLATION', None) conn.setdefault('TEST_NAME', None) + conn.setdefault('TEST_MIRROR', None) conn.setdefault('TIME_ZONE', settings.TIME_ZONE) for setting in ('NAME', 'USER', 'PASSWORD', 'HOST', 'PORT'): conn.setdefault(setting, '') Modified: django/trunk/django/test/simple.py =================================================================== --- django/trunk/django/test/simple.py 2010-01-24 00:10:30 UTC (rev 12288) +++ django/trunk/django/test/simple.py 2010-01-25 12:05:38 UTC (rev 12289) @@ -231,16 +231,30 @@ def setup_databases(self): from django.db import connections old_names = [] + mirrors = [] for alias in connections: connection = connections[alias] - old_names.append((connection, connection.settings_dict['NAME'])) - connection.creation.create_test_db(self.verbosity, autoclobber=not self.interactive) - return old_names + # If the database is a test mirror, redirect it's connection + # instead of creating a test database. + 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): 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: connection.creation.destroy_test_db(old_name, self.verbosity) @@ -273,11 +287,11 @@ suite = self.build_suite(test_labels, extra_tests) - old_names = self.setup_databases() + old_config = self.setup_databases() result = self.run_suite(suite) - self.teardown_databases(old_names) + self.teardown_databases(old_config) self.teardown_test_environment() Modified: django/trunk/docs/ref/settings.txt =================================================================== --- django/trunk/docs/ref/settings.txt 2010-01-24 00:10:30 UTC (rev 12288) +++ django/trunk/docs/ref/settings.txt 2010-01-25 12:05:38 UTC (rev 12289) @@ -357,6 +357,21 @@ .. _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 TEST_NAME Modified: django/trunk/docs/topics/testing.txt =================================================================== --- django/trunk/docs/topics/testing.txt 2010-01-24 00:10:30 UTC (rev 12288) +++ django/trunk/docs/topics/testing.txt 2010-01-25 12:05:38 UTC (rev 12289) @@ -301,12 +301,12 @@ when all the tests have been executed. 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 the tests will by default use an in-memory database (i.e., the database will be created in memory, bypassing the filesystem 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`. Aside from using a separate database, the test runner will otherwise @@ -325,6 +325,58 @@ :ref:`settings documentation <ref-settings>` for details of these 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 --------------------- @@ -1349,7 +1401,9 @@ 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) @@ -1357,10 +1411,14 @@ 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() Restores the pre-test environment. -- You received this message because you are subscribed to the Google Groups "Django updates" group. To post to this group, send email to django-upda...@googlegroups.com. To unsubscribe from this group, send email to django-updates+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/django-updates?hl=en.