为什么Django中对象主键在测试间递增?

9 投票
2 回答
3084 浏览
提问于 2025-04-30 18:30
class MyClassTest(TestCase):
    def setUp(self):
        Someclass.objects.create()

    def test_first_test(self):
        # Here, Someclass.objects.all()[0].pk -> returns 1

    def test_second_test(self):
       # Here, Someclass.objects.all()[0].pk -> returns 2 !!! (bad !)

SetUp()这个方法里,数据应该在每个测试之间被清空并重新创建。可是,为什么每次测试的ID会从一个增加到另一个呢?我觉得这不太明显。

这样一来,我就没办法根据ID来做测试,因为这些ID是依赖于其他测试的。所以我希望每次都能得到1作为结果。

需要注意的是,我对数据本身没有问题,旧的数据在每个测试之间都能很好地清除。问题只是出在ID上。

我在这里看到一篇文章,提到这个问题和数据库有关,而不是Django本身,但在Django里有没有什么办法可以改变这个情况呢?

暂无标签

2 个回答

11

在测试文档中有一个警告:

https://docs.djangoproject.com/en/dev/topics/testing/overview/

警告:如果你的测试需要访问数据库,比如创建或查询模型,确保你的测试类是从 django.test.TestCase 继承的,而不是 unittest.TestCase

使用 unittest.TestCase 可以避免每个测试都在一个事务中运行并清空数据库的开销,但如果你的测试与数据库有交互,它们的行为会根据测试运行的顺序而变化。这可能导致一些单元测试在单独运行时通过,但在一起运行时却失败。

你是使用 django.test.TestCase 还是 unittest.TestCase 呢?

如果你需要保持主键的完整性,似乎有一个选项可以尝试:

https://docs.djangoproject.com/en/dev/topics/testing/advanced/#django.test.TransactionTestCase.reset_sequences

TransactionTestCase 中设置 reset_sequences = True 可以确保在测试运行之前序列总是被重置:

class TestsThatDependsOnPrimaryKeySequences(TransactionTestCase):
    reset_sequences = True

    def test_animal_pk(self):
        lion = Animal.objects.create(name="lion", sound="roar")
        # lion.pk is guaranteed to always be 1
        self.assertEqual(lion.pk, 1)

因为 django.test.LiveServerTestCase 似乎是从 TransactionTestCase 继承的,所以这应该对你有效。

4

你很可能是在清除数据,包括你数据库里的数据。这和重新创建数据库或者重新创建序列是两回事。如果序列还在,它们会从上次停止的地方继续。

撰写回答