django测试中TestCase和TransactionTestCase类之间的区别

vis*_*m c 9 python django django-models django-testing

你能不能解释一下TestCase类和TransactionTestCase类之间的区别.我已阅读该文档但其唯一的说法是TestCase在数据库事务中运行测试并使用回滚来"撤消"数据库中的测试,如果需要手动管理测试中的事务,则需要使用django.test.TransactionTestCase .

请你帮我理解一个例子的实际差异吗?我只是想知道TestCase失败的条件是什么?还是回滚是自动发生还是我们必须写回滚声明?

请帮我

Jah*_*nov 9

TestCase和之间的主要区别TransactionTestCaseTestCaseatomic()块一直包装测试.从文档:

在两个嵌套的atomic()块中包含测试:一个用于整个类,一个用于每个测试

现在假设你有一个方法,如果它没有包含在atomic()块内,应该引发错误.您正在尝试为此编写测试:

def test_your_method_raises_error_without_atomic_block(self):
    with self.assertRaises(SomeError):
        your_method()
Run Code Online (Sandbox Code Playgroud)

这个测试意外失败!原因是,你猜对了,一直TestCaseatomic()块来包装测试.因此,your_method()不会引发错误,这就是为什么这个测试会失败的原因.在这种情况下,您应该使用TransactionTestCase来进行测试.

select_for_update()就是一个明显的例子:

在自动提交模式下使用select_for_update()在支持SELECT的后端上评估查询集... FOR UPDATE是一个TransactionManagementError错误

TransactionTestCase文档:

使用TestCase类,您无法测试事务中是否正在执行代码块,这在使用select_for_update()时是必需的

如果我们看一下文档select_for_update(),我们会看到一个警告:

尽管select_for_update()通常在自动提交模式下失败,但由于TestCase会自动将每个测试包装在事务中,因此即使在atomic()块之外调用TestCase中的select_for_update()也会(可能意外地)通过而不会引发TransactionManagementError.要正确测试select_for_update(),您应该使用TransactionTestCase.

希望能帮助到你!


小智 5

我想在这里发布一些示例和 django 代码,以便您了解如何TransactionTestCase工作TestCase

TransactionTestCaseTestCase继承自SimpleTestCase. 不同之处:

  • 运行测试时,TestCase会检查当前DB是否支持事务功能。如果为 true,将创建一个事务,并且所有测试代码现在都位于“事务块”下。在测试结束时,TestCase将回滚所有内容以保持数据库干净。阅读下面的setUp()tearDown()函数:

     @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()
    
    Run Code Online (Sandbox Code Playgroud)
  • TransactionTestCase但是,不会启动事务。它只是在所有测试完成后刷新数据库。

     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)
    
    Run Code Online (Sandbox Code Playgroud)

select_for_update()现在使用官方文档中提到的一些非常简单的示例:

    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)
Run Code Online (Sandbox Code Playgroud)

第一个将提出:

AssertionError:未引发 TransactionManagementError

第二个将顺利通过。