django 测试中 TestCase 和 TransactionTestCase 类 的区别
Difference between TestCase and TransactionTestCase classes in django test
请解释TestCase
class和TransactionTestCase
class的区别。我已经阅读了文档,但它只是说 TestCase
在数据库事务中运行测试并使用回滚来“撤消”数据库中的测试,如果您需要在测试中手动管理事务,则需要使用 django.test.TransactionTestCase
.
请通过示例帮助我了解实际差异。
TestCase
在什么情况下会失败?回滚是自动发生还是我们必须编写代码来执行回滚?
TestCase
和 TransactionTestCase
之间的主要区别是 TestCase
始终用 atomic()
块包装测试。来自 documentation:
Wraps the tests within two nested atomic() blocks: one for the whole class and one for each test
现在假设您有一个方法,如果它没有包含在 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()就是一个明显的例子:
Evaluating a queryset with select_for_update() in autocommit mode on backends which support SELECT ... FOR UPDATE is a TransactionManagementError error
来自TransactionTestCase documentation:
with TestCase class, you cannot test that a block of code is executing within a transaction, as is required when using select_for_update()
如果我们查看 select_for_update()
的文档,我们会看到一条警告:
Although select_for_update() normally fails in autocommit mode, since TestCase automatically wraps each test in a transaction, calling select_for_update() in a TestCase even outside an atomic() block will (perhaps unexpectedly) pass without raising a TransactionManagementError. To properly test select_for_update() you should use TransactionTestCase.
希望对您有所帮助!
我想在这里 post 一些示例和 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 not raised
第二个会顺利通过。
请解释TestCase
class和TransactionTestCase
class的区别。我已经阅读了文档,但它只是说 TestCase
在数据库事务中运行测试并使用回滚来“撤消”数据库中的测试,如果您需要在测试中手动管理事务,则需要使用 django.test.TransactionTestCase
.
请通过示例帮助我了解实际差异。
TestCase
在什么情况下会失败?回滚是自动发生还是我们必须编写代码来执行回滚?
TestCase
和 TransactionTestCase
之间的主要区别是 TestCase
始终用 atomic()
块包装测试。来自 documentation:
Wraps the tests within two nested atomic() blocks: one for the whole class and one for each test
现在假设您有一个方法,如果它没有包含在 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()就是一个明显的例子:
Evaluating a queryset with select_for_update() in autocommit mode on backends which support SELECT ... FOR UPDATE is a TransactionManagementError error
来自TransactionTestCase documentation:
with TestCase class, you cannot test that a block of code is executing within a transaction, as is required when using select_for_update()
如果我们查看 select_for_update()
的文档,我们会看到一条警告:
Although select_for_update() normally fails in autocommit mode, since TestCase automatically wraps each test in a transaction, calling select_for_update() in a TestCase even outside an atomic() block will (perhaps unexpectedly) pass without raising a TransactionManagementError. To properly test select_for_update() you should use TransactionTestCase.
希望对您有所帮助!
我想在这里 post 一些示例和 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 not raised
第二个会顺利通过。