在Django Rest Framework中执行Unittest时的TransactionManagementError

时间:2016-05-18 13:44:24

标签: django django-rest-framework django-testing

我编写了一个测试程序,用于检查在数据库中是否存在重复记录时是否引发了 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.
  

如何避免此错误,这肯定是不受欢迎的。

2 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:0)

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

属于TestCase子类的测试将您的测试用例和测试包装在db事务(原子块)中,并且您的视图以某种方式中断了该事务。对我来说,这是一个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,那么对于生产来说这可能不是问题,但是我仍然认为这是正确的解决方案。