summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJon Dufresne <jon.dufresne@gmail.com>2016-02-12 16:41:31 -0800
committerTim Graham <timograham@gmail.com>2016-02-13 06:53:39 -0500
commitfcd08c175787e909b3eb98f756317a07741c48dd (patch)
tree3a942d750d044adb6aed65358baca5a832eb03ff
parenta6f856df52d532d5537191eca237de6efdffe309 (diff)
Fixed #11665 -- Made TestCase check deferrable constraints after each test.
-rw-r--r--django/test/testcases.py13
-rw-r--r--docs/releases/1.10.txt6
-rw-r--r--docs/topics/testing/tools.txt7
-rw-r--r--tests/auth_tests/test_views.py6
-rw-r--r--tests/test_utils/test_testcase.py20
5 files changed, 50 insertions, 2 deletions
diff --git a/django/test/testcases.py b/django/test/testcases.py
index 34c94b82cc..9de29fff68 100644
--- a/django/test/testcases.py
+++ b/django/test/testcases.py
@@ -1044,7 +1044,18 @@ class TestCase(TransactionTestCase):
def _fixture_teardown(self):
if not connections_support_transactions():
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):
diff --git a/docs/releases/1.10.txt b/docs/releases/1.10.txt
index db4f7d4632..b3bd4b8afd 100644
--- a/docs/releases/1.10.txt
+++ b/docs/releases/1.10.txt
@@ -333,7 +333,8 @@ Templates
Tests
~~~~~
-* ...
+* To better catch bugs, :class:`~django.test.TestCase` now checks deferrable
+ database constraints at the end of each test.
URLs
~~~~
@@ -541,6 +542,9 @@ Miscellaneous
aggregate function now returns a ``float`` instead of ``decimal.Decimal``.
(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:
Features deprecated in 1.10
diff --git a/docs/topics/testing/tools.txt b/docs/topics/testing/tools.txt
index 5b8908c485..2bb53454cf 100644
--- a/docs/topics/testing/tools.txt
+++ b/docs/topics/testing/tools.txt
@@ -761,11 +761,18 @@ additions, including:
* Wraps the tests within two nested ``atomic`` blocks: one for the whole class
and one for each test.
+* Checks deferrable database constraints at the end of each test.
+
* Creates a TestClient instance.
* Django-specific assertions for testing for things like redirection and form
errors.
+.. versionchanged:: 1.10
+
+ The check for deferrable database constraints at the end of each test was
+ added.
+
.. classmethod:: TestCase.setUpTestData()
The class-level ``atomic`` block described above allows the creation of
diff --git a/tests/auth_tests/test_views.py b/tests/auth_tests/test_views.py
index 7fd493ca34..d8325d8b93 100644
--- a/tests/auth_tests/test_views.py
+++ b/tests/auth_tests/test_views.py
@@ -991,3 +991,9 @@ class UUIDUserTests(TestCase):
self.assertEqual(row.user_id, 1) # hardcoded in CustomUserAdmin.log_change()
self.assertEqual(row.object_id, str(u.pk))
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()
diff --git a/tests/test_utils/test_testcase.py b/tests/test_utils/test_testcase.py
new file mode 100644
index 0000000000..8a367391cb
--- /dev/null
+++ b/tests/test_utils/test_testcase.py
@@ -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