Commit b46dad1b authored by Adam Chainz's avatar Adam Chainz Committed by Tim Graham
Browse files

[1.8.x] Fixed #25176 -- Prevented TestCase.setUpTestData() exception from leaking transaction.

Backport of 0abb0693 from master
parent 1acdb881
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -958,7 +958,11 @@ class TestCase(TransactionTestCase):
                    except Exception:
                        cls._rollback_atomics(cls.cls_atomics)
                        raise
        try:
            cls.setUpTestData()
        except Exception:
            cls._rollback_atomics(cls.cls_atomics)
            raise

    @classmethod
    def tearDownClass(cls):
+3 −0
Original line number Diff line number Diff line
@@ -18,3 +18,6 @@ Bugfixes
* Fixed ``QuerySet.raw()`` so ``InvalidQuery`` is not raised when using the
  ``db_column`` name of a ``ForeignKey`` field with ``primary_key=True``
  (:ticket:`12768`).

* Prevented an exception in ``TestCase.setUpTestData()`` from leaking the
  transaction (:ticket:`25176`).
+33 −0
Original line number Diff line number Diff line
@@ -914,3 +914,36 @@ class OverrideSettingsTests(TestCase):
        with self.settings(STATICFILES_DIRS=[test_path]):
            finder = get_finder('django.contrib.staticfiles.finders.FileSystemFinder')
            self.assertIn(expected_location, finder.locations)


class TestBadSetUpTestData(TestCase):
    """
    An exception in setUpTestData() shouldn't leak a transaction which would
    cascade across the rest of the test suite.
    """
    class MyException(Exception):
        pass

    @classmethod
    def setUpClass(cls):
        try:
            super(TestBadSetUpTestData, cls).setUpClass()
        except cls.MyException:
            cls._in_atomic_block = connection.in_atomic_block

    @classmethod
    def tearDownClass(Cls):
        # override to avoid a second cls._rollback_atomics() which would fail.
        # Normal setUpClass() methods won't have exception handling so this
        # method wouldn't typically be run.
        pass

    @classmethod
    def setUpTestData(cls):
        # Simulate a broken setUpTestData() method.
        raise cls.MyException()

    def test_failure_in_setUpTestData_should_rollback_transaction(self):
        # setUpTestData() should call _rollback_atomics() so that the
        # transaction doesn't leak.
        self.assertFalse(self._in_atomic_block)