在 Django Rest Framework 中执行单元测试时出现 TransactionManagementError

TransactionManagementError While Executing Unittest in Django Rest Framework

我编写了一个测试,检查是否在数据库中出现重复记录时引发 IntegrityError。为了创建该场景,我发出了两次 REST API。代码如下所示:

class TestPost(APITestCase):
    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        common.add_users()

    def tearDown(self):
        super().tearDown()
        self.client.logout()

    def test_duplicate_record(self):
        # first time
        response = self.client.post('/api/v1/trees/', dict(alias="some name", path="some path"))
        # same request second time
        response = self.client.post('/api/v1/trees/', dict(alias="some name", path="some path"))
        self.assertEqual(response.status_code, status.HTTP_400_BAD_RREQUEST)

但是我得到这样的错误堆栈

 "An error occurred in the current transaction. You can't "
django.db.transaction.TransactionManagementError: An error occurred in the current transaction. You can't execute queries until the end of the 'atomic' block.

How can I avoid this error this is certainly undesirable.

尝试从 tearDown 方法中删除 self.client.logout()。 Django 在每次测试结束时回滚事务。您不必手动注销。

我今天遇到了这个问题,我花了一段时间才看到更大的图景,并妥善解决了它。确实,删除 self.client.logout() 可以解决问题,并且那里可能不需要,但问题出在您的观点上。

作为 TestCase 子类的测试将您的测试用例和测试包装在数据库事务(原子块)中,并且您的视图以某种方式破坏了该事务。对我来说,它吞下了一个 IntegrityError 异常。此时,事务已经中断,但 Django 并不知道,因此无法正确执行回滚。然后执行的任何查询都会导致 TransactionManagementError.

我的解决方法是将视图代码正确地包装在另一个原子块中:

try:
    with transaction.atomic():  # savepoint which will be rolled back
        counter.save()  # the operation which is expected to throw an IntegrityError
except IntegrityError:
    pass  # swallow the exception without breaking the transaction

如果您不使用 ATOMIC_REQUESTS,这对您在生产中可能不是问题,但我仍然认为这是正确的解决方案。