'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, TestCase will 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, TestCase will rollback all things to keep your DB clean. Read the setUp() and tearDown() 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