diff --git a/django/test/runner.py b/django/test/runner.py index 7dc28587af..ed969cc42f 100644 --- a/django/test/runner.py +++ b/django/test/runner.py @@ -11,7 +11,7 @@ from io import StringIO from django.core.management import call_command from django.db import connections -from django.test import SimpleTestCase, TestCase, TransactionTestCase +from django.test import SimpleTestCase, TestCase from django.test.utils import ( setup_databases as _setup_databases, setup_test_environment, teardown_databases as _teardown_databases, teardown_test_environment, @@ -399,7 +399,7 @@ class DiscoverRunner: parallel_test_suite = ParallelTestSuite test_runner = unittest.TextTestRunner test_loader = unittest.defaultTestLoader - reorder_by = (TestCase, TransactionTestCase, SimpleTestCase) + reorder_by = (TestCase, SimpleTestCase) def __init__(self, pattern=None, top_level=None, verbosity=1, interactive=True, failfast=False, keepdb=False, @@ -637,27 +637,6 @@ def is_discoverable(label): return os.path.isdir(os.path.abspath(label)) -def reorder_postprocess(reordered_suite): - """ - To make TransactionTestCases initialize their data properly, they must know - if the next TransactionTestCase needs initial data migrations serialized in - the connection. Initialize _next_serialized_rollback attribute depending on - the serialized_rollback option present in the next test class in the suite. - If the next test has no serialized_rollback attribute, it means there - aren't any more TransactionTestCases. - """ - # Filter out skipped tests. - active_tests = [ - test for test in reordered_suite._tests - if not getattr(test, '__unittest_skip__', False) - ] - for previous_test, next_test in zip(active_tests[:-1], active_tests[1:]): - next_serialized_rollback = getattr(next_test, 'serialized_rollback', None) - if next_serialized_rollback is not None: - previous_test._next_serialized_rollback = next_serialized_rollback - return reordered_suite - - def reorder_suite(suite, classes, reverse=False): """ Reorder a test suite by test type. @@ -677,7 +656,7 @@ def reorder_suite(suite, classes, reverse=False): reordered_suite = suite_class() for i in range(class_count + 1): reordered_suite.addTests(bins[i]) - return reorder_postprocess(reordered_suite) + return reordered_suite def partition_suite_by_type(suite, classes, bins, reverse=False): diff --git a/django/test/testcases.py b/django/test/testcases.py index 9f549f626f..36986185ce 100644 --- a/django/test/testcases.py +++ b/django/test/testcases.py @@ -827,15 +827,6 @@ class TransactionTestCase(SimpleTestCase): # This can be slow; this flag allows enabling on a per-case basis. serialized_rollback = False - # This attribute is strongly linked to serialized_rollback parameter and - # allows the data restoration after the database flush, at the end of the - # test, if the next test needs the initial data. This attribute is updated - # by the test runner when the test suite is built. Being initialized to - # True is crucial: the last TransactionTestCase, which doesn't have any - # test classes with the serialized_rollback attribute, will always have - # this value set to True. - _next_serialized_rollback = True - # Since tests will be wrapped in a transaction, or serialized if they # are not available, we allow queries to be run. allow_database_queries = True @@ -906,6 +897,16 @@ class TransactionTestCase(SimpleTestCase): if self.reset_sequences: self._reset_sequences(db_name) + # Provide replica initial data from migrated apps, if needed. + if self.serialized_rollback and hasattr(connections[db_name], "_test_serialized_contents"): + if self.available_apps is not None: + apps.unset_available_apps() + connections[db_name].creation.deserialize_db_from_string( + connections[db_name]._test_serialized_contents + ) + if self.available_apps is not None: + apps.set_available_apps(self.available_apps) + if self.fixtures: # We have to use this slightly awkward syntax due to the fact # that we're using *args and **kwargs together. @@ -959,15 +960,6 @@ class TransactionTestCase(SimpleTestCase): database=db_name, reset_sequences=False, allow_cascade=self.available_apps is not None, inhibit_post_migrate=inhibit_post_migrate) - # Provide replica initial data from migrated apps, if needed. - if self._next_serialized_rollback and hasattr(connections[db_name], '_test_serialized_contents'): - if self.available_apps is not None: - apps.unset_available_apps() - connections[db_name].creation.deserialize_db_from_string( - connections[db_name]._test_serialized_contents - ) - if self.available_apps is not None: - apps.set_available_apps(self.available_apps) def assertQuerysetEqual(self, qs, values, transform=repr, ordered=True, msg=None): items = map(transform, qs) diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt index 40f8a5cf49..5ebc26d868 100644 --- a/docs/ref/settings.txt +++ b/docs/ref/settings.txt @@ -777,11 +777,6 @@ the database state between tests if you don't have transactions). You can set this to ``False`` to speed up creation time if you don't have any test classes with :ref:`serialized_rollback=True `. -Don't set this to ``False`` if you want to use :option:`test --keepdb` -and your test suite contains :class:`~django.test.TransactionTestCase` or -doesn't support transactions, as this in-memory JSON string is used to restore -the initial data migrations in these situations. - .. setting:: TEST_TEMPLATE ``TEMPLATE`` diff --git a/tests/test_discovery_sample3/__init__.py b/tests/test_discovery_sample3/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/test_discovery_sample3/tests_transaction_test_case_mixed.py b/tests/test_discovery_sample3/tests_transaction_test_case_mixed.py deleted file mode 100644 index 13059bbb87..0000000000 --- a/tests/test_discovery_sample3/tests_transaction_test_case_mixed.py +++ /dev/null @@ -1,50 +0,0 @@ -from unittest import TestCase - -from django.test import ( - TestCase as DjangoTestCase, TransactionTestCase, skipUnlessDBFeature, -) - - -class TestVanillaUnittest(TestCase): - def test_sample(self): - self.assertEqual(1, 1) - - -class TestDjangoTestCase(DjangoTestCase): - def test_sample(self): - self.assertEqual(1, 1) - - -class TestTransactionTestCase1(TransactionTestCase): - available_apps = ['test_discovery_sample3'] - serialized_rollback = False - - def test_sample(self): - self.assertEqual(1, 1) - - -class TestTransactionTestCase2(TransactionTestCase): - available_apps = ['test_discovery_sample3'] - serialized_rollback = True - - def test_sample(self): - self.assertEqual(1, 1) - - -# django.test.runner.reorder_postprocess() ignores this skipped test when -# assigning _next_serialized_rollback. -@skipUnlessDBFeature('nonexistent') -class TestTransactionTestCase3(TransactionTestCase): - available_apps = ['test_discovery_sample3'] - serialized_rollback = True - - def test_sample(self): - self.assertEqual(1, 1) - - -class TestTransactionTestCase4(TransactionTestCase): - available_apps = ['test_discovery_sample3'] - serialized_rollback = False - - def test_sample(self): - self.assertEqual(1, 1) diff --git a/tests/test_discovery_sample3/tests_transaction_test_case_ordering.py b/tests/test_discovery_sample3/tests_transaction_test_case_ordering.py deleted file mode 100644 index 8d79bc08a6..0000000000 --- a/tests/test_discovery_sample3/tests_transaction_test_case_ordering.py +++ /dev/null @@ -1,28 +0,0 @@ -from unittest import TestCase - -from django.test import ( - SimpleTestCase, TestCase as DjangoTestCase, TransactionTestCase, -) - - -class TestDjangoTestCase(DjangoTestCase): - def test_sample(self): - self.assertEqual(1, 1) - - -class TestVanillaUnittest(TestCase): - def test_sample(self): - self.assertEqual(1, 1) - - -class TestZimpleTestCase(SimpleTestCase): - # Z gets this test to appear after Vanilla in the default suite. - def test_sample(self): - self.assertEqual(1, 1) - - -class TestTransactionTestCase(TransactionTestCase): - available_apps = ['test_discovery_sample3'] - - def test_sample(self): - self.assertEqual(1, 1) diff --git a/tests/test_runner/test_discover_runner.py b/tests/test_runner/test_discover_runner.py index efc055978d..d7569fc111 100644 --- a/tests/test_runner/test_discover_runner.py +++ b/tests/test_runner/test_discover_runner.py @@ -223,32 +223,3 @@ class DiscoverRunnerTests(SimpleTestCase): with captured_stdout() as stdout: runner.build_suite(['test_runner_apps.tagged.tests']) self.assertIn('Excluding test tag(s): bar, foo.\n', stdout.getvalue()) - - def test_transaction_test_case_before_simple_test_case(self): - runner = DiscoverRunner() - suite = runner.build_suite(['test_discovery_sample3.tests_transaction_test_case_ordering']) - suite = tuple(suite) - # TransactionTestCase is second after TestCase. - self.assertIn('TestTransactionTestCase', suite[1].id()) - - def test_transaction_test_case_next_serialized_rollback_option(self): - runner = DiscoverRunner() - suite = runner.build_suite(['test_discovery_sample3.tests_transaction_test_case_mixed']) - django_test_case, first_transaction_test_case, second_transaction_test_case, \ - third_transaction_test_case, fourth_transaction_test_case, vanilla_test_case = suite - # TransactionTestCase1._next_serialized_rollback is - # TransactionTestCase2.serialize_rollback. - self.assertEqual( - first_transaction_test_case._next_serialized_rollback, - second_transaction_test_case.serialized_rollback - ) - # TransactionTestCase2._next_serialized_rollback is - # TransactionTestCase4.serialize_rollback because TransactionTestCase3 - # is skipped. - self.assertEqual( - second_transaction_test_case._next_serialized_rollback, - fourth_transaction_test_case.serialized_rollback - ) - # The last TransactionTestCase of the suite has - # _next_serialized_rollback = True. - self.assertIs(fourth_transaction_test_case._next_serialized_rollback, True) diff --git a/tests/test_utils/test_transactiontestcase.py b/tests/test_utils/test_transactiontestcase.py index 193a4e299e..40c9b7576f 100644 --- a/tests/test_utils/test_transactiontestcase.py +++ b/tests/test_utils/test_transactiontestcase.py @@ -1,44 +1,10 @@ -import json from unittest import mock -from django.apps import apps from django.db import connections from django.test import TestCase, TransactionTestCase, override_settings -from .models import Car - -class TestSerializedContentMockMixin: - """ - Use this mixin on each test involving TransactionTestCase and - serialized_rollback = True option to avoid test dependencies. It mocks what - would be serialized after initial data migrations and restores it at the - end of the test. - """ - initial_data_migration = '[]' - _connections_test_serialized_content = {} - - def _pre_setup(self): - for db_name in self._databases_names(include_mirrors=False): - self._connections_test_serialized_content[db_name] = connections[db_name]._test_serialized_contents - connections[db_name]._test_serialized_contents = self.initial_data_migration - super()._pre_setup() - - def _post_teardown(self): - super()._post_teardown() - for db_name in self._databases_names(include_mirrors=False): - connections[db_name]._test_serialized_contents = self._connections_test_serialized_content[db_name] - - @classmethod - def tearDownClass(cls): - super().tearDownClass() - # Clean up any data that has been created by the class. - for data in json.loads(cls.initial_data_migration): - model = apps.get_model(*data['model'].split('.')) - model.objects.filter(pk=data['pk']).delete() - - -class TestSerializedRollbackInhibitsPostMigrate(TestSerializedContentMockMixin, TransactionTestCase): +class TestSerializedRollbackInhibitsPostMigrate(TransactionTestCase): """ TransactionTestCase._fixture_teardown() inhibits the post_migrate signal for test classes with serialized_rollback=True. @@ -78,39 +44,3 @@ class TransactionTestCaseMultiDbTests(TestCase): """ for alias in connections: self.assertEqual(len(connections[alias].queries_log), 0, 'Failed for alias %s' % alias) - - -class TestDataRestoredOnTearDownIfSerializedRollback(TestSerializedContentMockMixin, TransactionTestCase): - """ - Initial data is recreated in TransactionTestCase._fixture_teardown() - after the database is flushed so it's available in next test. - """ - available_apps = ['test_utils'] - _next_serialized_rollback = True - initial_data_migration = '[{"model": "test_utils.car", "pk": 666, "fields": {"name": "K 2000"}}]' - - def _post_teardown(self): - super()._post_teardown() - # Won't be True if running the tests with --reverse. - if self._next_serialized_rollback: - self.assertTrue(Car.objects.exists()) - - def test(self): - pass # Should be the only one in this class. - - -class TestDataNotRestoredOnTearDownIfNotSerializedRollback(TestSerializedContentMockMixin, TransactionTestCase): - """ - Initial data isn't recreated in TransactionTestCase._fixture_teardown() - if _next_serialized_rollback is False. - """ - available_apps = ['test_utils'] - _next_serialized_rollback = False - initial_data_migration = '[{"model": "test_utils.car", "pk": 666, "fields": {"name": "K 2000"}}]' - - def _post_teardown(self): - super()._post_teardown() - self.assertFalse(Car.objects.exists()) - - def test(self): - pass # Should be the only one in this class.