Refs #28478 -- Deprecated TestCase's allow_database_queries and multi_db in favor of databases.
This commit is contained in:
parent
647be06538
commit
8c775391b7
|
@ -4,9 +4,11 @@ import posixpath
|
|||
import sys
|
||||
import threading
|
||||
import unittest
|
||||
import warnings
|
||||
from collections import Counter
|
||||
from contextlib import contextmanager
|
||||
from copy import copy
|
||||
from difflib import get_close_matches
|
||||
from functools import wraps
|
||||
from unittest.util import safe_repr
|
||||
from urllib.parse import (
|
||||
|
@ -17,7 +19,7 @@ from urllib.request import url2pathname
|
|||
from django.apps import apps
|
||||
from django.conf import settings
|
||||
from django.core import mail
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.exceptions import ImproperlyConfigured, ValidationError
|
||||
from django.core.files import locks
|
||||
from django.core.handlers.wsgi import WSGIHandler, get_path_info
|
||||
from django.core.management import call_command
|
||||
|
@ -36,6 +38,7 @@ from django.test.utils import (
|
|||
override_settings,
|
||||
)
|
||||
from django.utils.decorators import classproperty
|
||||
from django.utils.deprecation import RemovedInDjango31Warning
|
||||
from django.views.static import serve
|
||||
|
||||
__all__ = ('TestCase', 'TransactionTestCase',
|
||||
|
@ -133,16 +136,31 @@ class _AssertTemplateNotUsedContext(_AssertTemplateUsedContext):
|
|||
|
||||
|
||||
class _CursorFailure:
|
||||
def __init__(self, cls_name, wrapped):
|
||||
self.cls_name = cls_name
|
||||
def __init__(self, wrapped, message):
|
||||
self.wrapped = wrapped
|
||||
self.message = message
|
||||
|
||||
def __call__(self):
|
||||
raise AssertionError(
|
||||
"Database queries aren't allowed in SimpleTestCase. "
|
||||
"Either use TestCase or TransactionTestCase to ensure proper test isolation or "
|
||||
"set %s.allow_database_queries to True to silence this failure." % self.cls_name
|
||||
)
|
||||
raise AssertionError(self.message)
|
||||
|
||||
|
||||
class _SimpleTestCaseDatabasesDescriptor:
|
||||
"""Descriptor for SimpleTestCase.allow_database_queries deprecation."""
|
||||
def __get__(self, instance, cls=None):
|
||||
try:
|
||||
allow_database_queries = cls.allow_database_queries
|
||||
except AttributeError:
|
||||
pass
|
||||
else:
|
||||
msg = (
|
||||
'`SimpleTestCase.allow_database_queries` is deprecated. '
|
||||
'Restrict the databases available during the execution of '
|
||||
'%s.%s with the `databases` attribute instead.'
|
||||
) % (cls.__module__, cls.__qualname__)
|
||||
warnings.warn(msg, RemovedInDjango31Warning)
|
||||
if allow_database_queries:
|
||||
return {DEFAULT_DB_ALIAS}
|
||||
return set()
|
||||
|
||||
|
||||
class SimpleTestCase(unittest.TestCase):
|
||||
|
@ -153,9 +171,13 @@ class SimpleTestCase(unittest.TestCase):
|
|||
_overridden_settings = None
|
||||
_modified_settings = None
|
||||
|
||||
# Tests shouldn't be allowed to query the database since
|
||||
# this base class doesn't enforce any isolation.
|
||||
allow_database_queries = False
|
||||
databases = _SimpleTestCaseDatabasesDescriptor()
|
||||
_disallowed_database_msg = (
|
||||
'Database queries are not allowed in SimpleTestCase subclasses. '
|
||||
'Either subclass TestCase or TransactionTestCase to ensure proper '
|
||||
'test isolation or add %(alias)r to %(test)s.databases to silence '
|
||||
'this failure.'
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
|
@ -166,19 +188,51 @@ class SimpleTestCase(unittest.TestCase):
|
|||
if cls._modified_settings:
|
||||
cls._cls_modified_context = modify_settings(cls._modified_settings)
|
||||
cls._cls_modified_context.enable()
|
||||
if not cls.allow_database_queries:
|
||||
for alias in connections:
|
||||
connection = connections[alias]
|
||||
connection.cursor = _CursorFailure(cls.__name__, connection.cursor)
|
||||
connection.chunked_cursor = _CursorFailure(cls.__name__, connection.chunked_cursor)
|
||||
cls._add_cursor_failures()
|
||||
|
||||
@classmethod
|
||||
def _validate_databases(cls):
|
||||
if cls.databases == '__all__':
|
||||
return frozenset(connections)
|
||||
for alias in cls.databases:
|
||||
if alias not in connections:
|
||||
message = '%s.%s.databases refers to %r which is not defined in settings.DATABASES.' % (
|
||||
cls.__module__,
|
||||
cls.__qualname__,
|
||||
alias,
|
||||
)
|
||||
close_matches = get_close_matches(alias, list(connections))
|
||||
if close_matches:
|
||||
message += ' Did you mean %r?' % close_matches[0]
|
||||
raise ImproperlyConfigured(message)
|
||||
return frozenset(cls.databases)
|
||||
|
||||
@classmethod
|
||||
def _add_cursor_failures(cls):
|
||||
cls.databases = cls._validate_databases()
|
||||
for alias in connections:
|
||||
if alias in cls.databases:
|
||||
continue
|
||||
connection = connections[alias]
|
||||
message = cls._disallowed_database_msg % {
|
||||
'test': '%s.%s' % (cls.__module__, cls.__qualname__),
|
||||
'alias': alias,
|
||||
}
|
||||
connection.cursor = _CursorFailure(connection.cursor, message)
|
||||
connection.chunked_cursor = _CursorFailure(connection.chunked_cursor, message)
|
||||
|
||||
@classmethod
|
||||
def _remove_cursor_failures(cls):
|
||||
for alias in connections:
|
||||
if alias in cls.databases:
|
||||
continue
|
||||
connection = connections[alias]
|
||||
connection.cursor = connection.cursor.wrapped
|
||||
connection.chunked_cursor = connection.chunked_cursor.wrapped
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
if not cls.allow_database_queries:
|
||||
for alias in connections:
|
||||
connection = connections[alias]
|
||||
connection.cursor = connection.cursor.wrapped
|
||||
connection.chunked_cursor = connection.chunked_cursor.wrapped
|
||||
cls._remove_cursor_failures()
|
||||
if hasattr(cls, '_cls_modified_context'):
|
||||
cls._cls_modified_context.disable()
|
||||
delattr(cls, '_cls_modified_context')
|
||||
|
@ -806,6 +860,26 @@ class SimpleTestCase(unittest.TestCase):
|
|||
self.fail(self._formatMessage(msg, standardMsg))
|
||||
|
||||
|
||||
class _TransactionTestCaseDatabasesDescriptor:
|
||||
"""Descriptor for TransactionTestCase.multi_db deprecation."""
|
||||
msg = (
|
||||
'`TransactionTestCase.multi_db` is deprecated. Databases available '
|
||||
'during this test can be defined using %s.%s.databases.'
|
||||
)
|
||||
|
||||
def __get__(self, instance, cls=None):
|
||||
try:
|
||||
multi_db = cls.multi_db
|
||||
except AttributeError:
|
||||
pass
|
||||
else:
|
||||
msg = self.msg % (cls.__module__, cls.__qualname__)
|
||||
warnings.warn(msg, RemovedInDjango31Warning)
|
||||
if multi_db:
|
||||
return set(connections)
|
||||
return {DEFAULT_DB_ALIAS}
|
||||
|
||||
|
||||
class TransactionTestCase(SimpleTestCase):
|
||||
|
||||
# Subclasses can ask for resetting of auto increment sequence before each
|
||||
|
@ -818,8 +892,12 @@ class TransactionTestCase(SimpleTestCase):
|
|||
# Subclasses can define fixtures which will be automatically installed.
|
||||
fixtures = None
|
||||
|
||||
# Do the tests in this class query non-default databases?
|
||||
multi_db = False
|
||||
databases = _TransactionTestCaseDatabasesDescriptor()
|
||||
_disallowed_database_msg = (
|
||||
'Database queries to %(alias)r are not allowed in this test. Add '
|
||||
'%(alias)r to %(test)s.databases to ensure proper test isolation '
|
||||
'and silence this failure.'
|
||||
)
|
||||
|
||||
# If transactions aren't available, Django will serialize the database
|
||||
# contents into a fixture during setup and flush and reload them
|
||||
|
@ -827,10 +905,6 @@ class TransactionTestCase(SimpleTestCase):
|
|||
# This can be slow; this flag allows enabling on a per-case basis.
|
||||
serialized_rollback = False
|
||||
|
||||
# Since tests will be wrapped in a transaction, or serialized if they
|
||||
# are not available, we allow queries to be run.
|
||||
allow_database_queries = True
|
||||
|
||||
def _pre_setup(self):
|
||||
"""
|
||||
Perform pre-test setup:
|
||||
|
@ -870,15 +944,13 @@ class TransactionTestCase(SimpleTestCase):
|
|||
|
||||
@classmethod
|
||||
def _databases_names(cls, include_mirrors=True):
|
||||
# If the test case has a multi_db=True flag, act on all databases,
|
||||
# including mirrors or not. Otherwise, just on the default DB.
|
||||
if cls.multi_db:
|
||||
return [
|
||||
alias for alias in connections
|
||||
if include_mirrors or not connections[alias].settings_dict['TEST']['MIRROR']
|
||||
]
|
||||
else:
|
||||
return [DEFAULT_DB_ALIAS]
|
||||
# Only consider allowed database aliases, including mirrors or not.
|
||||
return [
|
||||
alias for alias in connections
|
||||
if alias in cls.databases and (
|
||||
include_mirrors or not connections[alias].settings_dict['TEST']['MIRROR']
|
||||
)
|
||||
]
|
||||
|
||||
def _reset_sequences(self, db_name):
|
||||
conn = connections[db_name]
|
||||
|
@ -984,9 +1056,21 @@ class TransactionTestCase(SimpleTestCase):
|
|||
func(*args, **kwargs)
|
||||
|
||||
|
||||
def connections_support_transactions():
|
||||
"""Return True if all connections support transactions."""
|
||||
return all(conn.features.supports_transactions for conn in connections.all())
|
||||
def connections_support_transactions(aliases=None):
|
||||
"""
|
||||
Return whether or not all (or specified) connections support
|
||||
transactions.
|
||||
"""
|
||||
conns = connections.all() if aliases is None else (connections[alias] for alias in aliases)
|
||||
return all(conn.features.supports_transactions for conn in conns)
|
||||
|
||||
|
||||
class _TestCaseDatabasesDescriptor(_TransactionTestCaseDatabasesDescriptor):
|
||||
"""Descriptor for TestCase.multi_db deprecation."""
|
||||
msg = (
|
||||
'`TestCase.multi_db` is deprecated. Databases available during this '
|
||||
'test can be defined using %s.%s.databases.'
|
||||
)
|
||||
|
||||
|
||||
class TestCase(TransactionTestCase):
|
||||
|
@ -1002,6 +1086,8 @@ class TestCase(TransactionTestCase):
|
|||
On database backends with no transaction support, TestCase behaves as
|
||||
TransactionTestCase.
|
||||
"""
|
||||
databases = _TestCaseDatabasesDescriptor()
|
||||
|
||||
@classmethod
|
||||
def _enter_atomics(cls):
|
||||
"""Open atomic blocks for multiple databases."""
|
||||
|
@ -1018,10 +1104,14 @@ class TestCase(TransactionTestCase):
|
|||
transaction.set_rollback(True, using=db_name)
|
||||
atomics[db_name].__exit__(None, None, None)
|
||||
|
||||
@classmethod
|
||||
def _databases_support_transactions(cls):
|
||||
return connections_support_transactions(cls.databases)
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
if not connections_support_transactions():
|
||||
if not cls._databases_support_transactions():
|
||||
return
|
||||
cls.cls_atomics = cls._enter_atomics()
|
||||
|
||||
|
@ -1031,16 +1121,18 @@ class TestCase(TransactionTestCase):
|
|||
call_command('loaddata', *cls.fixtures, **{'verbosity': 0, 'database': db_name})
|
||||
except Exception:
|
||||
cls._rollback_atomics(cls.cls_atomics)
|
||||
cls._remove_cursor_failures()
|
||||
raise
|
||||
try:
|
||||
cls.setUpTestData()
|
||||
except Exception:
|
||||
cls._rollback_atomics(cls.cls_atomics)
|
||||
cls._remove_cursor_failures()
|
||||
raise
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
if connections_support_transactions():
|
||||
if cls._databases_support_transactions():
|
||||
cls._rollback_atomics(cls.cls_atomics)
|
||||
for conn in connections.all():
|
||||
conn.close()
|
||||
|
@ -1052,12 +1144,12 @@ class TestCase(TransactionTestCase):
|
|||
pass
|
||||
|
||||
def _should_reload_connections(self):
|
||||
if connections_support_transactions():
|
||||
if self._databases_support_transactions():
|
||||
return False
|
||||
return super()._should_reload_connections()
|
||||
|
||||
def _fixture_setup(self):
|
||||
if not connections_support_transactions():
|
||||
if not self._databases_support_transactions():
|
||||
# If the backend does not support transactions, we should reload
|
||||
# class data before each test
|
||||
self.setUpTestData()
|
||||
|
@ -1067,7 +1159,7 @@ class TestCase(TransactionTestCase):
|
|||
self.atomics = self._enter_atomics()
|
||||
|
||||
def _fixture_teardown(self):
|
||||
if not connections_support_transactions():
|
||||
if not self._databases_support_transactions():
|
||||
return super()._fixture_teardown()
|
||||
try:
|
||||
for db_name in reversed(self._databases_names()):
|
||||
|
|
|
@ -32,6 +32,9 @@ details on these changes.
|
|||
* ``RemoteUserBackend.configure_user()`` will require ``request`` as the first
|
||||
positional argument.
|
||||
|
||||
* Support for ``SimpleTestCase.allow_database_queries`` and
|
||||
``TransactionTestCase.multi_db`` will be removed.
|
||||
|
||||
.. _deprecation-removed-in-3.0:
|
||||
|
||||
3.0
|
||||
|
|
|
@ -513,3 +513,12 @@ Miscellaneous
|
|||
* :meth:`.RemoteUserBackend.configure_user` is now passed ``request`` as the
|
||||
first positional argument, if it accepts it. Support for overrides that don't
|
||||
accept it will be removed in Django 3.1.
|
||||
|
||||
* The :attr:`.SimpleTestCase.allow_database_queries`,
|
||||
:attr:`.TransactionTestCase.multi_db`, and :attr:`.TestCase.multi_db`
|
||||
attributes are deprecated in favor of :attr:`.SimpleTestCase.databases`,
|
||||
:attr:`.TransactionTestCase.databases`, and :attr:`.TestCase.databases`.
|
||||
These new attributes allow databases dependencies to be declared in order to
|
||||
prevent unexpected queries against non-default databases to leak state
|
||||
between tests. The previous behavior of ``allow_database_queries=True`` and
|
||||
``multi_db=True`` can be achieved by setting ``databases='__all__'``.
|
||||
|
|
|
@ -722,14 +722,24 @@ A subclass of :class:`unittest.TestCase` that adds this functionality:
|
|||
If your tests make any database queries, use subclasses
|
||||
:class:`~django.test.TransactionTestCase` or :class:`~django.test.TestCase`.
|
||||
|
||||
.. attribute:: SimpleTestCase.allow_database_queries
|
||||
.. attribute:: SimpleTestCase.databases
|
||||
|
||||
.. versionadded:: 2.2
|
||||
|
||||
:class:`~SimpleTestCase` disallows database queries by default. This
|
||||
helps to avoid executing write queries which will affect other tests
|
||||
since each ``SimpleTestCase`` test isn't run in a transaction. If you
|
||||
aren't concerned about this problem, you can disable this behavior by
|
||||
setting the ``allow_database_queries`` class attribute to ``True`` on
|
||||
your test class.
|
||||
setting the ``databases`` class attribute to ``'__all__'`` on your test
|
||||
class.
|
||||
|
||||
.. attribute:: SimpleTestCase.allow_database_queries
|
||||
|
||||
.. deprecated:: 2.2
|
||||
|
||||
This attribute is deprecated in favor of :attr:`databases`. The previous
|
||||
behavior of ``allow_database_queries = True`` can be achieved by setting
|
||||
``databases = '__all__'``.
|
||||
|
||||
.. warning::
|
||||
|
||||
|
@ -1101,8 +1111,8 @@ you can be certain that the outcome of a test will not be affected by another
|
|||
test or by the order of test execution.
|
||||
|
||||
By default, fixtures are only loaded into the ``default`` database. If you are
|
||||
using multiple databases and set :attr:`multi_db=True
|
||||
<TransactionTestCase.multi_db>`, fixtures will be loaded into all databases.
|
||||
using multiple databases and set :attr:`TransactionTestCase.databases`,
|
||||
fixtures will be loaded into all specified databases.
|
||||
|
||||
URLconf configuration
|
||||
---------------------
|
||||
|
@ -1119,7 +1129,9 @@ particular URL. Decorate your test class or test method with
|
|||
Multi-database support
|
||||
----------------------
|
||||
|
||||
.. attribute:: TransactionTestCase.multi_db
|
||||
.. attribute:: TransactionTestCase.databases
|
||||
|
||||
.. versionadded:: 2.2
|
||||
|
||||
Django sets up a test database corresponding to every database that is
|
||||
defined in the :setting:`DATABASES` definition in your settings
|
||||
|
@ -1133,24 +1145,67 @@ don't need to test multi-database activity.
|
|||
As an optimization, Django only flushes the ``default`` database at
|
||||
the start of each test run. If your setup contains multiple databases,
|
||||
and you have a test that requires every database to be clean, you can
|
||||
use the ``multi_db`` attribute on the test suite to request a full
|
||||
flush.
|
||||
use the ``databases`` attribute on the test suite to request extra databases
|
||||
to be flushed.
|
||||
|
||||
For example::
|
||||
|
||||
class TestMyViews(TestCase):
|
||||
multi_db = True
|
||||
class TestMyViews(TransactionTestCase):
|
||||
databases = {'default', 'other'}
|
||||
|
||||
def test_index_page_view(self):
|
||||
call_some_test_code()
|
||||
|
||||
This test case will flush *all* the test databases before running
|
||||
``test_index_page_view``.
|
||||
This test case will flush the ``default`` and ``other`` test databases before
|
||||
running ``test_index_page_view``. You can also use ``'__all__'`` to specify
|
||||
that all of the test databases must be flushed.
|
||||
|
||||
The ``multi_db`` flag also affects into which databases the
|
||||
:attr:`TransactionTestCase.fixtures` are loaded. By default (when
|
||||
``multi_db=False``), fixtures are only loaded into the ``default`` database.
|
||||
If ``multi_db=True``, fixtures are loaded into all databases.
|
||||
The ``databases`` flag also controls which databases the
|
||||
:attr:`TransactionTestCase.fixtures` are loaded into. By default, fixtures are
|
||||
only loaded into the ``default`` database.
|
||||
|
||||
Queries against databases not in ``databases`` will give assertion errors to
|
||||
prevent state leaking between tests.
|
||||
|
||||
.. attribute:: TransactionTestCase.multi_db
|
||||
|
||||
.. deprecated:: 2.2
|
||||
|
||||
This attribute is deprecated in favor of :attr:`~TransactionTestCase.databases`.
|
||||
The previous behavior of ``multi_db = True`` can be achieved by setting
|
||||
``databases = '__all__'``.
|
||||
|
||||
.. attribute:: TestCase.databases
|
||||
|
||||
.. versionadded:: 2.2
|
||||
|
||||
By default, only the ``default`` database will be wrapped in a transaction
|
||||
during a ``TestCase``'s execution and attempts to query other databases will
|
||||
result in assertion errors to prevent state leaking between tests.
|
||||
|
||||
Use the ``databases`` class attribute on the test class to request transaction
|
||||
wrapping against non-``default`` databases.
|
||||
|
||||
For example::
|
||||
|
||||
class OtherDBTests(TestCase):
|
||||
databases = {'other'}
|
||||
|
||||
def test_other_db_query(self):
|
||||
...
|
||||
|
||||
This test will only allow queries against the ``other`` database. Just like for
|
||||
:attr:`SimpleTestCase.databases` and :attr:`TransactionTestCase.databases`, the
|
||||
``'__all__'`` constant can be used to specify that the test should allow
|
||||
queries to all databases.
|
||||
|
||||
.. attribute:: TestCase.multi_db
|
||||
|
||||
.. deprecated:: 2.2
|
||||
|
||||
This attribute is deprecated in favor of :attr:`~TestCase.databases`. The
|
||||
previous behavior of ``multi_db = True`` can be achieved by setting
|
||||
``databases = '__all__'``.
|
||||
|
||||
.. _overriding-settings:
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ urlpatterns = [
|
|||
|
||||
@override_settings(ROOT_URLCONF=__name__, DATABASE_ROUTERS=['%s.Router' % __name__])
|
||||
class MultiDatabaseTests(TestCase):
|
||||
multi_db = True
|
||||
databases = {'default', 'other'}
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
|
|
@ -27,7 +27,7 @@ urlpatterns = [
|
|||
|
||||
@override_settings(ROOT_URLCONF=__name__, DATABASE_ROUTERS=['%s.Router' % __name__])
|
||||
class MultiDatabaseTests(TestCase):
|
||||
multi_db = True
|
||||
databases = {'default', 'other'}
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
|
|
@ -213,7 +213,7 @@ class ChangepasswordManagementCommandTestCase(TestCase):
|
|||
|
||||
|
||||
class MultiDBChangepasswordManagementCommandTestCase(TestCase):
|
||||
multi_db = True
|
||||
databases = {'default', 'other'}
|
||||
|
||||
@mock.patch.object(changepassword.Command, '_get_pass', return_value='not qwerty')
|
||||
def test_that_changepassword_command_with_database_option_uses_given_db(self, mock_get_pass):
|
||||
|
@ -906,7 +906,7 @@ class CreatesuperuserManagementCommandTestCase(TestCase):
|
|||
|
||||
|
||||
class MultiDBCreatesuperuserTestCase(TestCase):
|
||||
multi_db = True
|
||||
databases = {'default', 'other'}
|
||||
|
||||
def test_createsuperuser_command_with_database_option(self):
|
||||
"""
|
||||
|
|
|
@ -47,7 +47,7 @@ class LoadDataWithNaturalKeysTestCase(TestCase):
|
|||
|
||||
|
||||
class LoadDataWithNaturalKeysAndMultipleDatabasesTestCase(TestCase):
|
||||
multi_db = True
|
||||
databases = {'default', 'other'}
|
||||
|
||||
def test_load_data_with_user_permissions(self):
|
||||
# Create test contenttypes for both databases
|
||||
|
|
|
@ -1085,7 +1085,7 @@ class DBCacheRouter:
|
|||
},
|
||||
)
|
||||
class CreateCacheTableForDBCacheTests(TestCase):
|
||||
multi_db = True
|
||||
databases = {'default', 'other'}
|
||||
|
||||
@override_settings(DATABASE_ROUTERS=[DBCacheRouter()])
|
||||
def test_createcachetable_observes_database_router(self):
|
||||
|
|
|
@ -8,7 +8,7 @@ from django.test import TestCase
|
|||
|
||||
|
||||
class DatabaseCheckTests(TestCase):
|
||||
multi_db = True
|
||||
databases = {'default', 'other'}
|
||||
|
||||
@property
|
||||
def func(self):
|
||||
|
|
|
@ -214,7 +214,7 @@ class TestRouter:
|
|||
|
||||
@override_settings(DATABASE_ROUTERS=[TestRouter()])
|
||||
class ContentTypesMultidbTests(TestCase):
|
||||
multi_db = True
|
||||
databases = {'default', 'other'}
|
||||
|
||||
def test_multidb(self):
|
||||
"""
|
||||
|
|
|
@ -64,7 +64,7 @@ class DebugContextProcessorTests(TestCase):
|
|||
"""
|
||||
Tests for the ``django.template.context_processors.debug`` processor.
|
||||
"""
|
||||
multi_db = True
|
||||
databases = {'default', 'other'}
|
||||
|
||||
def test_debug(self):
|
||||
url = '/debug/'
|
||||
|
|
|
@ -341,7 +341,7 @@ class OtherRouter:
|
|||
|
||||
@override_settings(DATABASE_ROUTERS=[OtherRouter()])
|
||||
class LayerMapRouterTest(TestCase):
|
||||
multi_db = True
|
||||
databases = {'default', 'other'}
|
||||
|
||||
@unittest.skipUnless(len(settings.DATABASES) > 1, 'multiple databases required')
|
||||
def test_layermapping_default_db(self):
|
||||
|
|
|
@ -18,7 +18,7 @@ class MigrationTestBase(TransactionTestCase):
|
|||
"""
|
||||
|
||||
available_apps = ["migrations"]
|
||||
multi_db = True
|
||||
databases = {'default', 'other'}
|
||||
|
||||
def tearDown(self):
|
||||
# Reset applied-migrations state.
|
||||
|
|
|
@ -25,7 +25,7 @@ class MigrateTests(MigrationTestBase):
|
|||
"""
|
||||
Tests running the migrate command.
|
||||
"""
|
||||
multi_db = True
|
||||
databases = {'default', 'other'}
|
||||
|
||||
@override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations"})
|
||||
def test_migrate(self):
|
||||
|
|
|
@ -16,7 +16,7 @@ class RecorderTests(TestCase):
|
|||
"""
|
||||
Tests recording migrations as applied or not.
|
||||
"""
|
||||
multi_db = True
|
||||
databases = {'default', 'other'}
|
||||
|
||||
def test_apply(self):
|
||||
"""
|
||||
|
|
|
@ -38,7 +38,7 @@ class MigrateWhenFooRouter:
|
|||
|
||||
|
||||
class MultiDBOperationTests(OperationTestBase):
|
||||
multi_db = True
|
||||
databases = {'default', 'other'}
|
||||
|
||||
def _test_create_model(self, app_label, should_run):
|
||||
"""
|
||||
|
|
|
@ -17,7 +17,7 @@ from .routers import AuthRouter, TestRouter, WriteRouter
|
|||
|
||||
|
||||
class QueryTestCase(TestCase):
|
||||
multi_db = True
|
||||
databases = {'default', 'other'}
|
||||
|
||||
def test_db_selection(self):
|
||||
"Querysets will use the default database by default"
|
||||
|
@ -998,7 +998,7 @@ class ConnectionRouterTestCase(SimpleTestCase):
|
|||
# Make the 'other' database appear to be a replica of the 'default'
|
||||
@override_settings(DATABASE_ROUTERS=[TestRouter()])
|
||||
class RouterTestCase(TestCase):
|
||||
multi_db = True
|
||||
databases = {'default', 'other'}
|
||||
|
||||
def test_db_selection(self):
|
||||
"Querysets obey the router for db suggestions"
|
||||
|
@ -1526,7 +1526,7 @@ class RouterTestCase(TestCase):
|
|||
|
||||
@override_settings(DATABASE_ROUTERS=[AuthRouter()])
|
||||
class AuthTestCase(TestCase):
|
||||
multi_db = True
|
||||
databases = {'default', 'other'}
|
||||
|
||||
def test_auth_manager(self):
|
||||
"The methods on the auth manager obey database hints"
|
||||
|
@ -1589,7 +1589,7 @@ class AntiPetRouter:
|
|||
|
||||
|
||||
class FixtureTestCase(TestCase):
|
||||
multi_db = True
|
||||
databases = {'default', 'other'}
|
||||
fixtures = ['multidb-common', 'multidb']
|
||||
|
||||
@override_settings(DATABASE_ROUTERS=[AntiPetRouter()])
|
||||
|
@ -1629,7 +1629,7 @@ class FixtureTestCase(TestCase):
|
|||
|
||||
|
||||
class PickleQuerySetTestCase(TestCase):
|
||||
multi_db = True
|
||||
databases = {'default', 'other'}
|
||||
|
||||
def test_pickling(self):
|
||||
for db in connections:
|
||||
|
@ -1655,7 +1655,7 @@ class WriteToOtherRouter:
|
|||
|
||||
|
||||
class SignalTests(TestCase):
|
||||
multi_db = True
|
||||
databases = {'default', 'other'}
|
||||
|
||||
def override_router(self):
|
||||
return override_settings(DATABASE_ROUTERS=[WriteToOtherRouter()])
|
||||
|
@ -1755,7 +1755,7 @@ class AttributeErrorRouter:
|
|||
|
||||
|
||||
class RouterAttributeErrorTestCase(TestCase):
|
||||
multi_db = True
|
||||
databases = {'default', 'other'}
|
||||
|
||||
def override_router(self):
|
||||
return override_settings(DATABASE_ROUTERS=[AttributeErrorRouter()])
|
||||
|
@ -1807,7 +1807,7 @@ class ModelMetaRouter:
|
|||
|
||||
@override_settings(DATABASE_ROUTERS=[ModelMetaRouter()])
|
||||
class RouterModelArgumentTestCase(TestCase):
|
||||
multi_db = True
|
||||
databases = {'default', 'other'}
|
||||
|
||||
def test_m2m_collection(self):
|
||||
b = Book.objects.create(title="Pro Django",
|
||||
|
@ -1845,7 +1845,7 @@ class MigrateTestCase(TestCase):
|
|||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes'
|
||||
]
|
||||
multi_db = True
|
||||
databases = {'default', 'other'}
|
||||
|
||||
def test_migrate_to_other_database(self):
|
||||
"""Regression test for #16039: migrate with --database option."""
|
||||
|
@ -1879,7 +1879,7 @@ class RouterUsed(Exception):
|
|||
|
||||
|
||||
class RouteForWriteTestCase(TestCase):
|
||||
multi_db = True
|
||||
databases = {'default', 'other'}
|
||||
|
||||
class WriteCheckRouter:
|
||||
def db_for_write(self, model, **hints):
|
||||
|
@ -2093,7 +2093,7 @@ class NoRelationRouter:
|
|||
@override_settings(DATABASE_ROUTERS=[NoRelationRouter()])
|
||||
class RelationAssignmentTests(SimpleTestCase):
|
||||
"""allow_relation() is called with unsaved model instances."""
|
||||
multi_db = True
|
||||
databases = {'default', 'other'}
|
||||
router_prevents_msg = 'the current database router prevents this relation'
|
||||
|
||||
def test_foreign_key_relation(self):
|
||||
|
|
|
@ -1155,7 +1155,7 @@ class NullableTest(TestCase):
|
|||
|
||||
|
||||
class MultiDbTests(TestCase):
|
||||
multi_db = True
|
||||
databases = {'default', 'other'}
|
||||
|
||||
def test_using_is_honored_m2m(self):
|
||||
B = Book.objects.using('other')
|
||||
|
|
|
@ -209,8 +209,7 @@ class LiveServerPort(LiveServerBase):
|
|||
"Acquired duplicate server addresses for server threads: %s" % self.live_server_url
|
||||
)
|
||||
finally:
|
||||
if hasattr(TestCase, 'server_thread'):
|
||||
TestCase.server_thread.terminate()
|
||||
TestCase.tearDownClass()
|
||||
|
||||
def test_specified_port_bind(self):
|
||||
"""LiveServerTestCase.port customizes the server's port."""
|
||||
|
@ -227,8 +226,7 @@ class LiveServerPort(LiveServerBase):
|
|||
'Did not use specified port for LiveServerTestCase thread: %s' % TestCase.port
|
||||
)
|
||||
finally:
|
||||
if hasattr(TestCase, 'server_thread'):
|
||||
TestCase.server_thread.terminate()
|
||||
TestCase.tearDownClass()
|
||||
|
||||
|
||||
class LiverServerThreadedTests(LiveServerBase):
|
||||
|
|
|
@ -18,7 +18,7 @@ from django.test.utils import captured_stdout
|
|||
|
||||
@modify_settings(INSTALLED_APPS={'append': 'django.contrib.sites'})
|
||||
class SitesFrameworkTests(TestCase):
|
||||
multi_db = True
|
||||
databases = {'default', 'other'}
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
@ -236,7 +236,7 @@ class JustOtherRouter:
|
|||
|
||||
@modify_settings(INSTALLED_APPS={'append': 'django.contrib.sites'})
|
||||
class CreateDefaultSiteTests(TestCase):
|
||||
multi_db = True
|
||||
databases = {'default', 'other'}
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
|
|
@ -241,8 +241,8 @@ class Ticket17477RegressionTests(AdminScriptTestCase):
|
|||
|
||||
|
||||
class SQLiteInMemoryTestDbs(TransactionTestCase):
|
||||
multi_db = True
|
||||
available_apps = ['test_runner']
|
||||
databases = {'default', 'other'}
|
||||
|
||||
@unittest.skipUnless(all(db.connections[conn].vendor == 'sqlite' for conn in db.connections),
|
||||
"This is an sqlite-specific issue")
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
from django.db import connections
|
||||
from django.db.utils import DEFAULT_DB_ALIAS
|
||||
from django.test import SimpleTestCase, TestCase, TransactionTestCase
|
||||
from django.utils.deprecation import RemovedInDjango31Warning
|
||||
|
||||
|
||||
class AllowDatabaseQueriesDeprecationTests(SimpleTestCase):
|
||||
def test_enabled(self):
|
||||
class AllowedDatabaseQueries(SimpleTestCase):
|
||||
allow_database_queries = True
|
||||
message = (
|
||||
'`SimpleTestCase.allow_database_queries` is deprecated. Restrict '
|
||||
'the databases available during the execution of '
|
||||
'test_utils.test_deprecated_features.AllowDatabaseQueriesDeprecationTests.'
|
||||
'test_enabled.<locals>.AllowedDatabaseQueries with the '
|
||||
'`databases` attribute instead.'
|
||||
)
|
||||
with self.assertWarnsMessage(RemovedInDjango31Warning, message):
|
||||
self.assertEqual(AllowedDatabaseQueries.databases, {'default'})
|
||||
|
||||
def test_explicitly_disabled(self):
|
||||
class AllowedDatabaseQueries(SimpleTestCase):
|
||||
allow_database_queries = False
|
||||
message = (
|
||||
'`SimpleTestCase.allow_database_queries` is deprecated. Restrict '
|
||||
'the databases available during the execution of '
|
||||
'test_utils.test_deprecated_features.AllowDatabaseQueriesDeprecationTests.'
|
||||
'test_explicitly_disabled.<locals>.AllowedDatabaseQueries with '
|
||||
'the `databases` attribute instead.'
|
||||
)
|
||||
with self.assertWarnsMessage(RemovedInDjango31Warning, message):
|
||||
self.assertEqual(AllowedDatabaseQueries.databases, set())
|
||||
|
||||
|
||||
class MultiDbDeprecationTests(SimpleTestCase):
|
||||
def test_transaction_test_case(self):
|
||||
class MultiDbTestCase(TransactionTestCase):
|
||||
multi_db = True
|
||||
message = (
|
||||
'`TransactionTestCase.multi_db` is deprecated. Databases '
|
||||
'available during this test can be defined using '
|
||||
'test_utils.test_deprecated_features.MultiDbDeprecationTests.'
|
||||
'test_transaction_test_case.<locals>.MultiDbTestCase.databases.'
|
||||
)
|
||||
with self.assertWarnsMessage(RemovedInDjango31Warning, message):
|
||||
self.assertEqual(MultiDbTestCase.databases, set(connections))
|
||||
MultiDbTestCase.multi_db = False
|
||||
with self.assertWarnsMessage(RemovedInDjango31Warning, message):
|
||||
self.assertEqual(MultiDbTestCase.databases, {DEFAULT_DB_ALIAS})
|
||||
|
||||
def test_test_case(self):
|
||||
class MultiDbTestCase(TestCase):
|
||||
multi_db = True
|
||||
message = (
|
||||
'`TestCase.multi_db` is deprecated. Databases available during '
|
||||
'this test can be defined using '
|
||||
'test_utils.test_deprecated_features.MultiDbDeprecationTests.'
|
||||
'test_test_case.<locals>.MultiDbTestCase.databases.'
|
||||
)
|
||||
with self.assertWarnsMessage(RemovedInDjango31Warning, message):
|
||||
self.assertEqual(MultiDbTestCase.databases, set(connections))
|
||||
MultiDbTestCase.multi_db = False
|
||||
with self.assertWarnsMessage(RemovedInDjango31Warning, message):
|
||||
self.assertEqual(MultiDbTestCase.databases, {DEFAULT_DB_ALIAS})
|
|
@ -1,7 +1,7 @@
|
|||
from django.db import IntegrityError, transaction
|
||||
from django.test import TestCase, skipUnlessDBFeature
|
||||
|
||||
from .models import PossessedCar
|
||||
from .models import Car, PossessedCar
|
||||
|
||||
|
||||
class TestTestCase(TestCase):
|
||||
|
@ -18,3 +18,12 @@ class TestTestCase(TestCase):
|
|||
car.delete()
|
||||
finally:
|
||||
self._rollback_atomics = rollback_atomics
|
||||
|
||||
def test_disallowed_database_queries(self):
|
||||
message = (
|
||||
"Database queries to 'other' are not allowed in this test. "
|
||||
"Add 'other' to test_utils.test_testcase.TestTestCase.databases to "
|
||||
"ensure proper test isolation and silence this failure."
|
||||
)
|
||||
with self.assertRaisesMessage(AssertionError, message):
|
||||
Car.objects.using('other').get()
|
||||
|
|
|
@ -3,6 +3,8 @@ from unittest import mock
|
|||
from django.db import connections
|
||||
from django.test import TestCase, TransactionTestCase, override_settings
|
||||
|
||||
from .models import Car
|
||||
|
||||
|
||||
class TestSerializedRollbackInhibitsPostMigrate(TransactionTestCase):
|
||||
"""
|
||||
|
@ -32,9 +34,9 @@ class TestSerializedRollbackInhibitsPostMigrate(TransactionTestCase):
|
|||
|
||||
|
||||
@override_settings(DEBUG=True) # Enable query logging for test_queries_cleared
|
||||
class TransactionTestCaseMultiDbTests(TestCase):
|
||||
class TransactionTestCaseDatabasesTests(TestCase):
|
||||
available_apps = []
|
||||
multi_db = True
|
||||
databases = {'default', 'other'}
|
||||
|
||||
def test_queries_cleared(self):
|
||||
"""
|
||||
|
@ -44,3 +46,17 @@ class TransactionTestCaseMultiDbTests(TestCase):
|
|||
"""
|
||||
for alias in connections:
|
||||
self.assertEqual(len(connections[alias].queries_log), 0, 'Failed for alias %s' % alias)
|
||||
|
||||
|
||||
class DisallowedDatabaseQueriesTests(TransactionTestCase):
|
||||
available_apps = ['test_utils']
|
||||
|
||||
def test_disallowed_database_queries(self):
|
||||
message = (
|
||||
"Database queries to 'other' are not allowed in this test. "
|
||||
"Add 'other' to test_utils.test_transactiontestcase."
|
||||
"DisallowedDatabaseQueriesTests.databases to ensure proper test "
|
||||
"isolation and silence this failure."
|
||||
)
|
||||
with self.assertRaisesMessage(AssertionError, message):
|
||||
Car.objects.using('other').get()
|
||||
|
|
|
@ -7,8 +7,9 @@ from unittest import mock
|
|||
from django.conf import settings
|
||||
from django.contrib.staticfiles.finders import get_finder, get_finders
|
||||
from django.contrib.staticfiles.storage import staticfiles_storage
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.core.files.storage import default_storage
|
||||
from django.db import connection, models, router
|
||||
from django.db import connection, connections, models, router
|
||||
from django.forms import EmailField, IntegerField
|
||||
from django.http import HttpResponse
|
||||
from django.template.loader import render_to_string
|
||||
|
@ -1160,32 +1161,67 @@ class TestBadSetUpTestData(TestCase):
|
|||
class DisallowedDatabaseQueriesTests(SimpleTestCase):
|
||||
def test_disallowed_database_queries(self):
|
||||
expected_message = (
|
||||
"Database queries aren't allowed in SimpleTestCase. "
|
||||
"Either use TestCase or TransactionTestCase to ensure proper test isolation or "
|
||||
"set DisallowedDatabaseQueriesTests.allow_database_queries to True to silence this failure."
|
||||
"Database queries are not allowed in SimpleTestCase subclasses. "
|
||||
"Either subclass TestCase or TransactionTestCase to ensure proper "
|
||||
"test isolation or add 'default' to "
|
||||
"test_utils.tests.DisallowedDatabaseQueriesTests.databases to "
|
||||
"silence this failure."
|
||||
)
|
||||
with self.assertRaisesMessage(AssertionError, expected_message):
|
||||
Car.objects.first()
|
||||
|
||||
|
||||
class DisallowedDatabaseQueriesChunkedCursorsTests(SimpleTestCase):
|
||||
def test_disallowed_database_queries(self):
|
||||
def test_disallowed_database_chunked_cursor_queries(self):
|
||||
expected_message = (
|
||||
"Database queries aren't allowed in SimpleTestCase. Either use "
|
||||
"TestCase or TransactionTestCase to ensure proper test isolation or "
|
||||
"set DisallowedDatabaseQueriesChunkedCursorsTests.allow_database_queries "
|
||||
"to True to silence this failure."
|
||||
"Database queries are not allowed in SimpleTestCase subclasses. "
|
||||
"Either subclass TestCase or TransactionTestCase to ensure proper "
|
||||
"test isolation or add 'default' to "
|
||||
"test_utils.tests.DisallowedDatabaseQueriesTests.databases to "
|
||||
"silence this failure."
|
||||
)
|
||||
with self.assertRaisesMessage(AssertionError, expected_message):
|
||||
next(Car.objects.iterator())
|
||||
|
||||
|
||||
class AllowedDatabaseQueriesTests(SimpleTestCase):
|
||||
allow_database_queries = True
|
||||
databases = {'default'}
|
||||
|
||||
def test_allowed_database_queries(self):
|
||||
Car.objects.first()
|
||||
|
||||
def test_allowed_database_chunked_cursor_queries(self):
|
||||
next(Car.objects.iterator(), None)
|
||||
|
||||
|
||||
class DatabaseAliasTests(SimpleTestCase):
|
||||
def setUp(self):
|
||||
self.addCleanup(setattr, self.__class__, 'databases', self.databases)
|
||||
|
||||
def test_no_close_match(self):
|
||||
self.__class__.databases = {'void'}
|
||||
message = (
|
||||
"test_utils.tests.DatabaseAliasTests.databases refers to 'void' which is not defined "
|
||||
"in settings.DATABASES."
|
||||
)
|
||||
with self.assertRaisesMessage(ImproperlyConfigured, message):
|
||||
self._validate_databases()
|
||||
|
||||
def test_close_match(self):
|
||||
self.__class__.databases = {'defualt'}
|
||||
message = (
|
||||
"test_utils.tests.DatabaseAliasTests.databases refers to 'defualt' which is not defined "
|
||||
"in settings.DATABASES. Did you mean 'default'?"
|
||||
)
|
||||
with self.assertRaisesMessage(ImproperlyConfigured, message):
|
||||
self._validate_databases()
|
||||
|
||||
def test_match(self):
|
||||
self.__class__.databases = {'default', 'other'}
|
||||
self.assertEqual(self._validate_databases(), frozenset({'default', 'other'}))
|
||||
|
||||
def test_all(self):
|
||||
self.__class__.databases = '__all__'
|
||||
self.assertEqual(self._validate_databases(), frozenset(connections))
|
||||
|
||||
|
||||
@isolate_apps('test_utils', attr_name='class_apps')
|
||||
class IsolatedAppsTests(SimpleTestCase):
|
||||
|
|
|
@ -225,7 +225,7 @@ class DebugViewTests(SimpleTestCase):
|
|||
|
||||
class DebugViewQueriesAllowedTests(SimpleTestCase):
|
||||
# May need a query to initialize MySQL connection
|
||||
allow_database_queries = True
|
||||
databases = {'default'}
|
||||
|
||||
def test_handle_db_exception(self):
|
||||
"""
|
||||
|
|
Loading…
Reference in New Issue