【问题标题】:Difference between TestCase and TransactionTestCase classes in django testdjango测试中TestCase和TransactionTestCase类的区别
【发布时间】:2017-11-11 01:02:06
【问题描述】:

请解释TestCase 类和TransactionTestCase 类之间的区别。我已经阅读了文档,但它只是说TestCase 在数据库事务中运行测试并使用回滚来“撤消”数据库中的测试,如果您需要在测试中手动管理事务,则需要使用 @ 987654324@.

请通过示例帮助我了解实际差异。 TestCase 在什么情况下会失败?回滚是自动发生的还是我们必须编写代码来执行回滚?

【问题讨论】:

    标签: python django django-models django-testing


    【解决方案1】:

    TestCase 和TransactionTestCase 之间的主要区别在于TestCase 始终使用atomic() 块包装测试。来自documentation:

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

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

    def test_your_method_raises_error_without_atomic_block(self):
        with self.assertRaises(SomeError):
            your_method()
    

    此测试将意外失败!原因是,您猜对了,TestCase 始终使用atomic() 块包装测试。因此,your_method() 不会引发错误,这就是该测试失败的原因。在这种情况下,您应该使用 TransactionTestCase 使您的测试通过。

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

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

    来自TransactionTestCase documentation:

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

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

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

    希望对你有帮助!

    【讨论】:

    • 是的,它很有帮助! ,我有很多疑问。显然我无法理解交易。如果您不介意,您能否给我您的联系方式以便完美聊天。?因为我不能在这里聊天
    • 抱歉,我在您的个人资料中没有找到任何有关联系人的信息。这就是我在这里问的原因。请考虑我
    • @vishnumc 给我发电子邮件 jahongir@mysuperdispatch.com
    • 谢谢。请您考虑我的电子邮件
    【解决方案2】:

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

    TransactionTestCase 和 TestCase 都继承自 SimpleTestCase。区别:

    • 运行测试时,TestCase 将检查当前数据库是否支持事务功能。如果为真,则将创建一个事务,并且所有测试代码现在都位于“事务块”下。在测试结束时,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()
      
    • 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)
      

    现在官方文档中提到了一些使用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)
    

    第一个会加注:

    AssertionError: TransactionManagementError 未引发

    第二个会顺利通过。

    【讨论】:

      猜你喜欢
      • 2016-11-02
      • 2011-02-04
      • 2017-09-21
      • 2021-12-02
      • 1970-01-01
      • 2017-11-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多