我编写了一个测试程序,用于检查在数据库中是否存在重复记录时是否引发了 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.
如何避免此错误,这肯定是不受欢迎的。
答案 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
,那么对于生产来说这可能不是问题,但是我仍然认为这是正确的解决方案。