Fixed #20579 -- Improved TransactionTestCase.available_apps.

Also moved its documentation to the 'advanced' section. It doesn't
belong to the 'overview'. Same for TransactionTestCase.reset_sequences.

When available_apps is set, after a TransactionTestCase, the database
is now totally empty. post_syncdb is fired at the beginning of the next
TransactionTestCase.

Refs #20483.
This commit is contained in:
Aymeric Augustin 2013-06-11 22:56:09 +02:00
parent 0938970491
commit 55cbd65985
4 changed files with 130 additions and 87 deletions

View file

@ -155,6 +155,80 @@ If there are any circular dependencies in the
:setting:`TEST_DEPENDENCIES` definition, an ``ImproperlyConfigured``
exception will be raised.
Advanced features of ``TransactionTestCase``
============================================
.. currentmodule:: django.test
.. attribute:: TransactionTestCase.available_apps
.. versionadded:: 1.6
.. warning::
This attribute is a private API. It may be changed or removed without
a deprecation period in the future, for instance to accomodate changes
in application loading.
It's used to optimize Django's own test suite, which contains hundreds
of models but no relations between models in different applications.
By default, ``available_apps`` is set to ``None``. After each test, Django
calls :djadmin:`flush` to reset the database state. This empties all tables
and emits the :data:`~django.db.models.signals.post_syncdb` signal, which
re-creates one content type and three permissions for each model. This
operation gets expensive proportionally to the number of models.
Setting ``available_apps`` to a list of applications instructs Django to
behave as if only the models from these applications were available. The
behavior of ``TransactionTestCase`` changes as follows:
- :data:`~django.db.models.signals.post_syncdb` is fired before each
test to create the content types and permissions for each model in
available apps, in case they're missing.
- After each test, Django empties only tables corresponding to models in
available apps. However, at the database level, truncation may cascade to
related models in unavailable apps. Furthermore
:data:`~django.db.models.signals.post_syncdb` isn't fired; it will be
fired by the next ``TransactionTestCase``, after the correct set of
applications is selected.
Since the database isn't fully flushed, if a test creates instances of
models not included in ``available_apps``, they will leak and they may
cause unrelated tests to fail. Be careful with tests that use sessions;
the default session engine stores them in the database.
Since :data:`~django.db.models.signals.post_syncdb` isn't emitted after
flushing the database, its state after a ``TransactionTestCase`` isn't the
same as after a ``TestCase``: it's missing the rows created by listeners
to :data:`~django.db.models.signals.post_syncdb`. Considering the
:ref:`order in which tests are executed <order-of-tests>`, this isn't an
issue, provided either all ``TransactionTestCase`` in a given test suite
declare ``available_apps``, or none of them.
``available_apps`` is mandatory in Django's own test suite.
.. attribute:: TransactionTestCase.reset_sequences
.. versionadded:: 1.5
Setting ``reset_sequences = True`` on a ``TransactionTestCase`` will make
sure sequences are always reset before the test run::
class TestsThatDependsOnPrimaryKeySequences(TransactionTestCase):
reset_sequences = True
def test_animal_pk(self):
lion = Animal.objects.create(name="lion", sound="roar")
# lion.pk is guaranteed to always be 1
self.assertEqual(lion.pk, 1)
Unless you are explicitly testing primary keys sequence numbers, it is
recommended that you do not hard code primary key values in tests.
Using ``reset_sequences = True`` will slow down the test, since the primary
key reset is an relatively expensive database operation.
Running tests outside the test runner
=====================================

View file

@ -213,6 +213,8 @@ advanced settings.
The :ref:`advanced multi-db testing topics <topics-testing-advanced-multidb>`.
.. _order-of-tests:
Order in which tests are executed
---------------------------------
@ -908,8 +910,8 @@ TransactionTestCase
.. class:: TransactionTestCase()
Django ``TestCase`` classes make use of database transaction facilities, if
available, to speed up the process of resetting the database to a known state
Django's ``TestCase`` class (described below) makes use of database transaction
facilities to speed up the process of resetting the database to a known state
at the beginning of each test. A consequence of this, however, is that the
effects of transaction commit and rollback cannot be tested by a Django
``TestCase`` class. If your test requires testing of such transactional
@ -927,9 +929,9 @@ to test the effects of commit and rollback:
Instead, it encloses the test code in a database transaction that is rolled
back at the end of the test. Both explicit commits like
``transaction.commit()`` and implicit ones that may be caused by
``Model.save()`` are replaced with a ``nop`` operation. This guarantees that
the rollback at the end of the test restores the database to its initial
state.
``transaction.atomic()`` are replaced with a ``nop`` operation. This
guarantees that the rollback at the end of the test restores the database to
its initial state.
When running on a database that does not support rollback (e.g. MySQL with the
MyISAM storage engine), ``TestCase`` falls back to initializing the database
@ -940,22 +942,21 @@ to test the effects of commit and rollback:
While ``commit`` and ``rollback`` operations still *appear* to work when
used in ``TestCase``, no actual commit or rollback will be performed by the
database. This can cause your tests to pass or fail unexpectedly. Always
use ``TransactionalTestCase`` when testing transactional behavior.
use ``TransactionTestCase`` when testing transactional behavior.
.. note::
.. versionchanged:: 1.5
.. versionchanged:: 1.5
Prior to 1.5, ``TransactionTestCase`` flushed the database tables *before*
each test. In Django 1.5, this is instead done *after* the test has been run.
Prior to 1.5, :class:`~django.test.TransactionTestCase` flushed the
database tables *before* each test. In Django 1.5, this is instead done
*after* the test has been run.
When the flush took place before the test, it was guaranteed that primary
key values started at one in :class:`~django.test.TransactionTestCase`
tests.
Tests should not depend on this behavior, but for legacy tests that do, the
:attr:`~TransactionTestCase.reset_sequences` attribute can be used until
the test has been properly updated.
Tests should not depend on this behavior, but for legacy tests that do,
the :attr:`~TransactionTestCase.reset_sequences` attribute can be used
until the test has been properly updated.
.. versionchanged:: 1.5
@ -964,55 +965,6 @@ to test the effects of commit and rollback:
``TransactionTestCase`` inherits from :class:`~django.test.SimpleTestCase`.
.. attribute:: TransactionTestCase.reset_sequences
.. versionadded:: 1.5
Setting ``reset_sequences = True`` on a ``TransactionTestCase`` will make
sure sequences are always reset before the test run::
class TestsThatDependsOnPrimaryKeySequences(TransactionTestCase):
reset_sequences = True
def test_animal_pk(self):
lion = Animal.objects.create(name="lion", sound="roar")
# lion.pk is guaranteed to always be 1
self.assertEqual(lion.pk, 1)
Unless you are explicitly testing primary keys sequence numbers, it is
recommended that you do not hard code primary key values in tests.
Using ``reset_sequences = True`` will slow down the test, since the primary
key reset is an relatively expensive database operation.
.. attribute:: TransactionTestCase.available_apps
.. warning::
This attribute is a private API. It may be changed or removed without
a deprecation period in the future, for instance to accomodate changes
in application loading.
It's used to optimize Django's own test suite, which contains hundreds
of models but no relations between models in different applications.
.. versionadded:: 1.6
By default, ``available_apps`` is set to ``None`` and has no effect.
Setting it to a list of applications tells Django to behave as if only the
models from these applications were available:
- Before each test, Django creates content types and permissions only for
these models.
- After each test, Django flushes only the corresponding tables. However,
at the database level, truncation may cascade to other related models,
even if they aren't in ``available_apps``.
Since the database isn't fully flushed, if a test creates instances of
models not included in ``available_apps``, they will leak and they may
cause unrelated tests to fail. Be careful with tests that use sessions;
the default session engine stores them in the database.
TestCase
~~~~~~~~
@ -1495,7 +1447,7 @@ Emptying the test outbox
~~~~~~~~~~~~~~~~~~~~~~~~
If you use any of Django's custom ``TestCase`` classes, the test runner will
clear thecontents of the test email outbox at the start of each test case.
clear the contents of the test email outbox at the start of each test case.
For more detail on email services during tests, see `Email services`_ below.