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