diff --git a/django/test/testcases.py b/django/test/testcases.py
index ecf0eda4e8..c960d88828 100644
--- a/django/test/testcases.py
+++ b/django/test/testcases.py
@@ -958,7 +958,11 @@ class TestCase(TransactionTestCase):
                     except Exception:
                         cls._rollback_atomics(cls.cls_atomics)
                         raise
-        cls.setUpTestData()
+        try:
+            cls.setUpTestData()
+        except Exception:
+            cls._rollback_atomics(cls.cls_atomics)
+            raise
 
     @classmethod
     def tearDownClass(cls):
diff --git a/docs/releases/1.8.4.txt b/docs/releases/1.8.4.txt
index 81b00c1800..53c58e6a90 100644
--- a/docs/releases/1.8.4.txt
+++ b/docs/releases/1.8.4.txt
@@ -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`).
diff --git a/tests/test_utils/tests.py b/tests/test_utils/tests.py
index 1c5c12ca5b..ae8d34b691 100644
--- a/tests/test_utils/tests.py
+++ b/tests/test_utils/tests.py
@@ -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)