commit 967d5501a6f0b166e78c471c80aabc117e12a914
Author: Raphaël Barrois <raphael.barrois@polytechnique.org>
Date:   Sat May 11 00:26:34 2013 +0200

    test.TestCase: Use a single transaction per class.
    
    And wrap each test_XXX in a atomic() call.

diff --git a/django/test/testcases.py b/django/test/testcases.py
index a9fcc2b..d4bde8d 100644
--- a/django/test/testcases.py
+++ b/django/test/testcases.py
@@ -448,6 +448,9 @@ class TransactionTestCase(SimpleTestCase):
     # test case
     reset_sequences = False
 
+    # Subclasses can ask for connection closure after each test or each TestCase
+    reset_connections_per_test = True
+
     def _pre_setup(self):
         """Performs any pre-test setup. This includes:
 
@@ -463,10 +466,11 @@ class TransactionTestCase(SimpleTestCase):
         self._urlconf_setup()
         mail.outbox = []
 
-    def _databases_names(self, include_mirrors=True):
+    @classmethod
+    def _databases_names(cls, include_mirrors=True):
         # If the test case has a multi_db=True flag, act on all databases,
         # including mirrors or not. Otherwise, just on the default DB.
-        if getattr(self, 'multi_db', False):
+        if getattr(cls, 'multi_db', False):
             return [alias for alias in connections
                     if include_mirrors or not connections[alias].settings_dict['TEST_MIRROR']]
         else:
@@ -519,6 +523,17 @@ class TransactionTestCase(SimpleTestCase):
         # be created with the wrong time).
         # To make sure this doesn't happen, get a clean connection at the
         # start of every test.
+        if self.reset_connections_per_test:
+            for conn in connections.all():
+                conn.close()
+
+    @classmethod
+    def tearDownClass(cls):
+        super(TransactionTestCase, cls).tearDownClass()
+
+        # Ensure connections are closed, no matter what.
+        # django.db.backends.BaseDatabaseWrapper.close() behaves properly
+        # on already closed connections.
         for conn in connections.all():
             conn.close()
 
@@ -818,6 +833,10 @@ class TestCase(TransactionTestCase):
     inside a test.
     """
 
+    # For savepoints to be useful, connections should only be closed at
+    # tearDownClass().
+    reset_connections_per_test = False
+
     def _fixture_setup(self):
         if not connections_support_transactions():
             return super(TestCase, self)._fixture_setup()
@@ -828,8 +847,6 @@ class TestCase(TransactionTestCase):
         for db_name in self._databases_names():
             self.atomics[db_name] = transaction.atomic(using=db_name)
             self.atomics[db_name].__enter__()
-        # Remove this when the legacy transaction management goes away.
-        disable_transaction_methods()
 
         for db in self._databases_names(include_mirrors=False):
             if hasattr(self, 'fixtures'):
@@ -845,12 +862,45 @@ class TestCase(TransactionTestCase):
         if not connections_support_transactions():
             return super(TestCase, self)._fixture_teardown()
 
+        for db_name in reversed(self._databases_names()):
+            if connections[db_name].connection is None:
+                # Already closed
+                continue
+
+            # Hack to force a rollback: populate the exception fields
+            self.atomics[db_name].__exit__(ValueError, "Rollback", None)
+
+    @classmethod
+    def setUpClass(cls):
+        if not connections_support_transactions():
+            return super(TestCase, cls).setUpClass()
+
+        cls.class_atomics = {}
+        for db_name in cls._databases_names(include_mirrors=False):
+            cls.class_atomics[db_name] = transaction.atomic(using=db_name)
+            cls.class_atomics[db_name].__enter__()
+
+        # Remove this when the legacy transaction management goes away.
+        disable_transaction_methods()
+
+        return super(TestCase, cls).setUpClass()
+
+    @classmethod
+    def tearDownClass(cls):
+        if not connections_support_transactions():
+            return super(TestCase, cls).tearDownClass()
+
         # Remove this when the legacy transaction management goes away.
         restore_transaction_methods()
-        for db_name in reversed(self._databases_names()):
-            # Hack to force a rollback
-            connections[db_name].needs_rollback = True
-            self.atomics[db_name].__exit__(None, None, None)
+
+        for db_name in reversed(cls._databases_names()):
+            if connections[db_name].connection is None:
+                # Already closed
+                continue
+
+            cls.class_atomics[db_name].__exit__(ValueError, "Rollback", None)
+
+        return super(TestCase, cls).tearDownClass()
 
 
 def _deferredSkip(condition, reason):
diff --git a/docs/topics/testing/overview.txt b/docs/topics/testing/overview.txt
index 9228a07..272c976 100644
--- a/docs/topics/testing/overview.txt
+++ b/docs/topics/testing/overview.txt
@@ -987,7 +987,10 @@ additions, including:
 
 * Automatic loading of fixtures.
 
-* Wraps each test in a transaction.
+* Wraps each TestCase in a single transaction, and each test in a
+  SAVEPOINT/ROLLBACK pair; this allows creating objects within
+  :meth:`~django.test.TestCase.setUpClass` instead of
+  :meth:`~django.test.TestCase.setUp`.
 
 * Creates a TestClient instance.
 
@@ -999,6 +1002,15 @@ additions, including:
     The order in which tests are run has changed. See `Order in which tests are
     executed`_.
 
+.. versionchanged:: 1.6
+
+    Prior to 1.6, each test ran inside a dedicated transaction.
+    A transaction is now opened at :meth:`~django.test.TestCase.setUpClass`,
+    and each test runs within a savepoint.
+
+    This allows model creation in :meth:`~django.test.TestCase.setUpClass`,
+    that will stay unaltered by each test's code.
+
 ``TestCase`` inherits from :class:`~django.test.TransactionTestCase`.
 
 .. _live-test-server:
diff --git a/tests/test_utils/tests.py b/tests/test_utils/tests.py
index d72a482..a8a7665 100644
--- a/tests/test_utils/tests.py
+++ b/tests/test_utils/tests.py
@@ -598,6 +598,59 @@ class SkippingExtraTests(TestCase):
         pass
 
 
+class AtomicSetUpClass1(TestCase):
+    person_pk = 0
+
+    @classmethod
+    def setUpClass(cls):
+        super(AtomicSetUpClass1, cls).setUpClass()
+        person = Person.objects.create(name='setUpClass1')
+        if cls.person_pk == 0:
+            cls.person_pk = person.pk
+
+    def test_setupclass_called(self):
+        p = Person.objects.get(name='setUpClass1')
+        self.assertEqual(self.person_pk, p.pk)
+
+    def test_setupclass_called_only_once(self):
+        """Ensure the Person is created only once."""
+        p = Person.objects.get(name='setUpClass1')
+        self.assertEqual(self.person_pk, p.pk)
+
+    def test_other_setupclass_rolled_back(self):
+        """
+        Ensure the Person created by AtomicSetUpClass2 doesn't exist.
+        """
+        persons = list(Person.objects.all())
+        self.assertEqual(1, len(persons))
+        person = persons[0]
+        self.assertEqual(self.person_pk, person.pk)
+        self.assertEqual('setUpClass1', person.name)
+
+
+class AtomicSetUpClass2(TestCase):
+    """
+    Second TestCase, to ensure that setUpClass is rolled back between TestCase.
+    """
+    person_pk = 0
+    @classmethod
+    def setUpClass(cls):
+        super(AtomicSetUpClass2, cls).setUpClass()
+        person = Person.objects.create(name='setUpClass2')
+        if cls.person_pk == 0:
+            cls.person_pk = person.pk
+
+    def test_other_setupclass_rolled_back(self):
+        """
+        Ensure the Person created by AtomicSetUpClass1 doesn't exist anymore.
+        """
+        persons = list(Person.objects.all())
+        self.assertEqual(1, len(persons))
+        person = persons[0]
+        self.assertEqual(self.person_pk, person.pk)
+        self.assertEqual('setUpClass2', person.name)
+
+
 class AssertRaisesMsgTest(SimpleTestCase):
 
     def test_special_re_chars(self):
