Fixed #11665 -- Made TestCase check deferrable constraints after each test.

This commit is contained in:
Jon Dufresne 2016-02-12 16:41:31 -08:00 committed by Tim Graham
parent a6f856df52
commit fcd08c1757
5 changed files with 50 additions and 2 deletions

View File

@ -1044,7 +1044,18 @@ class TestCase(TransactionTestCase):
def _fixture_teardown(self): def _fixture_teardown(self):
if not connections_support_transactions(): if not connections_support_transactions():
return super(TestCase, self)._fixture_teardown() return super(TestCase, self)._fixture_teardown()
self._rollback_atomics(self.atomics) try:
for db_name in reversed(self._databases_names()):
if self._should_check_constraints(connections[db_name]):
connections[db_name].check_constraints()
finally:
self._rollback_atomics(self.atomics)
def _should_check_constraints(self, connection):
return (
connection.features.can_defer_constraint_checks and
not connection.needs_rollback and connection.is_usable()
)
class CheckCondition(object): class CheckCondition(object):

View File

@ -333,7 +333,8 @@ Templates
Tests Tests
~~~~~ ~~~~~
* ... * To better catch bugs, :class:`~django.test.TestCase` now checks deferrable
database constraints at the end of each test.
URLs URLs
~~~~ ~~~~
@ -541,6 +542,9 @@ Miscellaneous
aggregate function now returns a ``float`` instead of ``decimal.Decimal``. aggregate function now returns a ``float`` instead of ``decimal.Decimal``.
(It's still wrapped in a measure of square meters.) (It's still wrapped in a measure of square meters.)
* Tests that violate deferrable database constraints will now error when run on
a database that supports deferrable constraints.
.. _deprecated-features-1.10: .. _deprecated-features-1.10:
Features deprecated in 1.10 Features deprecated in 1.10

View File

@ -761,11 +761,18 @@ additions, including:
* Wraps the tests within two nested ``atomic`` blocks: one for the whole class * Wraps the tests within two nested ``atomic`` blocks: one for the whole class
and one for each test. and one for each test.
* Checks deferrable database constraints at the end of each test.
* Creates a TestClient instance. * Creates a TestClient instance.
* Django-specific assertions for testing for things like redirection and form * Django-specific assertions for testing for things like redirection and form
errors. errors.
.. versionchanged:: 1.10
The check for deferrable database constraints at the end of each test was
added.
.. classmethod:: TestCase.setUpTestData() .. classmethod:: TestCase.setUpTestData()
The class-level ``atomic`` block described above allows the creation of The class-level ``atomic`` block described above allows the creation of

View File

@ -991,3 +991,9 @@ class UUIDUserTests(TestCase):
self.assertEqual(row.user_id, 1) # hardcoded in CustomUserAdmin.log_change() self.assertEqual(row.user_id, 1) # hardcoded in CustomUserAdmin.log_change()
self.assertEqual(row.object_id, str(u.pk)) self.assertEqual(row.object_id, str(u.pk))
self.assertEqual(row.get_change_message(), 'Changed password.') self.assertEqual(row.get_change_message(), 'Changed password.')
# The LogEntry.user column isn't altered to a UUID type so it's set to
# an integer manually in CustomUserAdmin to avoid an error. To avoid a
# constraint error, delete the entry before constraints are checked
# after the test.
row.delete()

View File

@ -0,0 +1,20 @@
from django.db import IntegrityError, transaction
from django.test import TestCase, skipUnlessDBFeature
from .models import PossessedCar
class TestTestCase(TestCase):
@skipUnlessDBFeature('can_defer_constraint_checks')
@skipUnlessDBFeature('supports_foreign_keys')
def test_fixture_teardown_checks_constraints(self):
rollback_atomics = self._rollback_atomics
self._rollback_atomics = lambda connection: None # noop
try:
car = PossessedCar.objects.create(car_id=1, belongs_to_id=1)
with self.assertRaises(IntegrityError), transaction.atomic():
self._fixture_teardown()
car.delete()
finally:
self._rollback_atomics = rollback_atomics