在同一个Django unittest测试用例中测试几个IntegrityErrors

时间:2014-07-05 16:41:39

标签: django unit-testing testing django-models

我正在尝试运行以下Django unittest:

class MyModelTests(TestCase):
    def test_failed_duplicate(self):
        m = MyModel.objects.create(a='a', b='a')
        with self.assertRaises(IntegrityError):
            MyModel.objects.create(a='a', b='b')
        with self.assertRaises(IntegrityError):
            MyModel.objects.create(a='a', b='c')
        with self.assertRaises(IntegrityError):
            MyModel.objects.create(a='a', b='d')

由于违反了字段a上的唯一性约束,有几项测试都会失败。 (我对这些断言进行了一些模糊处理,但它们都测试了a的不同值,这些值应该失败。)

然而,在跑步时我得到:

Traceback (most recent call last):
  File "...", line 21, in test_failed_duplicate
    MyModel.objects.create(a='a', b='c')
TransactionManagementError: An error occurred in the current transaction. You can't execute queries until the end of the 'atomic' block.

我错过了什么?

1 个答案:

答案 0 :(得分:23)

(啊,是的。我在一段时间后遇到了这个问题并提交了a ticket。你可能会发现讨论很有趣。)

您之所以看到这一点,是因为:

  1. 出于性能原因,TestCase测试在事务中运行。
  2. IntegrityError的加注会破坏当前的交易(更准确地说,是当前的原子区块),无论是否被捕获。
  3. 因此,在您的代码中,第一个assertRaises正常工作,但由于IntegrityError被引发,因此交易变得被破坏。当您尝试使用下一个create()访问数据库时,您会获得TransactionManagementError

    有两种可能的解决方案:

    1. 继承自TransactionTestCase而非TestCase。这使用表截断而不是事务来重置数据库,因此您不会遇到此问题。
    2. 在新的原子块中运行每个create()测试。您可以在故障单中查看示例,并在the docs中查看更详细的说明。