'Difference between TestCase and TransactionTestCase classes in django test
Please explain the difference between the TestCase class and TransactionTestCase class. I have read the documentation but it's only saying that TestCase runs tests in a database transaction and uses rollback to "undo" the test in the database, and if you need to manually manage transactions within your test, you would need to use django.test.TransactionTestCase.
Please help me understand the actual difference with an example.
In what condition will TestCase fail? Do rollbacks happen automatically or do we have to write code to do the rollback?
Solution 1:[1]
I would like to post some example and django code here so that you can know how TransactionTestCase and TestCase work.
Both TransactionTestCase and TestCase are inherit from SimpleTestCase. Difference:
When runing the test,
TestCasewill check if the current DB support transaction feature. If true, a transaction will be created and all test code are now under a "transaction block". And at the end of the test,TestCasewill rollback all things to keep your DB clean. Read thesetUp()andtearDown()functions below:@classmethod def setUpClass(cls): super(TestCase, cls).setUpClass() if not connections_support_transactions(): return 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(): cls._rollback_atomics(cls.cls_atomics) for conn in connections.all(): conn.close() super(TestCase, cls).tearDownClass()TransactionTestCase, however, does not start a transaction. It simply flushes the DB after all tests done.def _post_teardown(self): try: self._fixture_teardown() super(TransactionTestCase, self)._post_teardown() if self._should_reload_connections(): for conn in connections.all(): conn.close() finally: if self.available_apps is not None: apps.unset_available_apps() setting_changed.send(sender=settings._wrapped.__class__, setting='INSTALLED_APPS', value=settings.INSTALLED_APPS, enter=False) def _fixture_teardown(self): for db_name in self._databases_names(include_mirrors=False): call_command('flush', verbosity=0, interactive=False, database=db_name, reset_sequences=False, allow_cascade=self.available_apps is not None, inhibit_post_migrate=self.available_apps is not None)
Now some very simple example using select_for_update() mentioned in official docs:
class SampleTestCase(TestCase):
def setUp(self):
Sample.objects.create(**{'field1': 'value1', 'field2': 'value2'})
def test_difference_testcase(self):
sample = Sample.objects.select_for_update().filter()
print(sample)
class SampleTransactionTestCase(TransactionTestCase):
def setUp(self):
Sample.objects.create(**{'field1': 'value1', 'field2': 'value2'})
def test_difference_transactiontestcase(self):
sample = Sample.objects.select_for_update().filter()
print(sample)
The first one will raise:
AssertionError: TransactionManagementError not raised
And the second one will pass without an error.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Mohd |
