django test

时间:2017-06-09 06:14:59

标签: python django django-models django-testing

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

请您通过示例帮助我了解实际差异吗? 我只是想知道TestCase失败的条件是什么?还有回滚是自动发生还是我们必须写回滚声明?

请帮帮我

2 个答案:

答案 0 :(得分:8)

TestCaseTransactionTestCase之间的主要区别在于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。

希望它有所帮助!

答案 1 :(得分:0)

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

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

  • 在运行测试时,TestCase将检查当前数据库是否支持事务功能。如果为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()
    
  • 但是,
  • 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

第二个会顺利通过。