From 119154ca7f65d423bb742aa0e32ffcfcb21d4b87 Mon Sep 17 00:00:00 2001 From: Thomas Chaumeny Date: Thu, 4 Dec 2014 14:05:59 +0100 Subject: [PATCH] Refs #20392 -- Load fixtures once within TestCase --- django/test/testcases.py | 60 +++++++++++++++++++++------------------- docs/releases/1.8.txt | 23 ++++++++++++--- 2 files changed, 50 insertions(+), 33 deletions(-) diff --git a/django/test/testcases.py b/django/test/testcases.py index 78c185ecd8..38bbd40946 100644 --- a/django/test/testcases.py +++ b/django/test/testcases.py @@ -915,24 +915,46 @@ class TestCase(TransactionTestCase): On database backends with no transaction support, TestCase behaves as TransactionTestCase. """ + @classmethod + def _enter_atomics(cls): + """Helper method to open atomic blocks for multiple databases""" + atomics = {} + for db_name in cls._databases_names(): + atomics[db_name] = transaction.atomic(using=db_name) + atomics[db_name].__enter__() + return atomics + + @classmethod + def _rollback_atomics(cls, atomics): + """Rollback atomic blocks opened through the previous method""" + for db_name in reversed(cls._databases_names()): + transaction.set_rollback(True, using=db_name) + atomics[db_name].__exit__(None, None, None) @classmethod def setUpClass(cls): super(TestCase, cls).setUpClass() if not connections_support_transactions(): return - cls.cls_atomics = {} - for db_name in cls._databases_names(): - cls.cls_atomics[db_name] = transaction.atomic(using=db_name) - cls.cls_atomics[db_name].__enter__() + cls.cls_atomics = cls._enter_atomics() + + if cls.fixtures: + for db_name in cls._databases_names(include_mirrors=False): + try: + call_command('loaddata', *cls.fixtures, **{ + 'verbosity': 0, + 'commit': False, + 'database': db_name, + }) + except Exception: + cls._rollback_atomics(cls.cls_atomics) + raise cls.setUpTestData() @classmethod def tearDownClass(cls): if connections_support_transactions(): - for db_name in reversed(cls._databases_names()): - transaction.set_rollback(True, using=db_name) - cls.cls_atomics[db_name].__exit__(None, None, None) + cls._rollback_atomics(cls.cls_atomics) for conn in connections.all(): conn.close() super(TestCase, cls).tearDownClass() @@ -955,32 +977,12 @@ class TestCase(TransactionTestCase): return super(TestCase, self)._fixture_setup() assert not self.reset_sequences, 'reset_sequences cannot be used on TestCase instances' - - self.atomics = {} - for db_name in self._databases_names(): - self.atomics[db_name] = transaction.atomic(using=db_name) - self.atomics[db_name].__enter__() - - for db_name in self._databases_names(include_mirrors=False): - if self.fixtures: - try: - call_command('loaddata', *self.fixtures, - **{ - 'verbosity': 0, - 'commit': False, - 'database': db_name, - }) - except Exception: - self._fixture_teardown() - raise + self.atomics = self._enter_atomics() def _fixture_teardown(self): if not connections_support_transactions(): return super(TestCase, self)._fixture_teardown() - - for db_name in reversed(self._databases_names()): - transaction.set_rollback(True, using=db_name) - self.atomics[db_name].__exit__(None, None, None) + self._rollback_atomics(self.atomics) class CheckCondition(object): diff --git a/docs/releases/1.8.txt b/docs/releases/1.8.txt index 5ab42996ab..f9eaf83148 100644 --- a/docs/releases/1.8.txt +++ b/docs/releases/1.8.txt @@ -67,6 +67,25 @@ to accept expressions other than aggregates. Aggregates are now able to reference multiple fields, as well as perform arithmetic, similar to ``F()`` objects. +``TestCase`` data setup +~~~~~~~~~~~~~~~~~~~~~~~ + +:class:`~django.test.TestCase` has been refactored to allow for data +initialization at the class level using transactions and savepoints. Database +backends which do not support transactions, like MySQL with the MyISAM storage +engine, will still be able to run these tests but won't benefit from the +improvements. Tests are now run within two nested +:func:`~django.db.transaction.atomic()` blocks: one for the whole class and one +for each test. + +* The class method + :meth:`TestCase.setUpTestData() ` adds + the ability to setup test data at the class level. Using this technique can + speed up the tests as compared to using ``setUp()``. + +* Fixture loading within ``TestCase`` is now performed once for the whole + ``TestCase``. + Minor features ~~~~~~~~~~~~~~ @@ -515,10 +534,6 @@ Tests * The :func:`~django.test.override_settings` decorator can now affect the master router in :setting:`DATABASE_ROUTERS`. -* Added the ability to setup test data at the class level using - :meth:`TestCase.setUpTestData() `. Using - this technique can speed up the tests as compared to using ``setUp()``. - * Added test client support for file uploads with file-like objects. Validators