【问题标题】:Unusual error in Django tests wrongly thinking instance doesn't existDjango 测试中的异常错误错误地认为实例不存在
【发布时间】:2015-06-07 22:06:53
【问题描述】:

我有一个非常定制的 Django 应用程序,它检查用户是否可以将 ForeignKey 更改为某些值。

在这种情况下,User 属于WorkgroupItem 也可以属于Workgroup,因此,当User 生成Item 时,他们只能把它在他们所属的Workgroups 中。复杂的事情Item是一个父类,所以“Item”的类型很多。

目前我有一个自定义管理表单设置来检查这个:

class AdminConceptForm(autocomplete_light.ModelForm):
    def __init__(self, *args, **kwargs):
        #... other code
        self.fields['workgroup'].queryset = self.request.user.profile.editable_workgroups.all()

这个测试的重要部分是:

def setUp(self):
    from django.test import Client

    self.client = Client()
    self.wg1 = models.Workgroup.objects.create(name="Test WG 1") # Editor is member

def test_editor_change_item(self):
    self.login_editor()
    response = self.client.get(reverse("admin:%s_%s_change"%(self.itemType._meta.app_label,self.itemType._meta.model_name),args=[self.item1.pk]))
    self.assertResponseStatusCodeEqual(response,200)

    updated_item = dict((k,v) for (k,v) in model_to_dict(self.item1).items() if v is not None)
    updated_name = updated_item['name'] + " updated!"
    updated_item['name'] = updated_name

    updated_item.update({
        'statuses-TOTAL_FORMS': 0, 'statuses-INITIAL_FORMS': 0 #no statuses
    })
    updated_item.update(self.form_defaults)
    self.assertTrue(self.wg1 in self.editor.profile.myWorkgroups)

    self.assertEqual([self.wg1],list(response.context['adminform'].form.fields['workgroup'].queryset))

    self.assertTrue(perms.user_can_edit(self.editor,self.item1))
    self.assertTrue(self.item1.workgroup in self.editor.profile.editable_workgroups.all())

    response = self.client.post(
            reverse("admin:%s_%s_change"%(self.itemType._meta.app_label,self.itemType._meta.model_name),args=[self.item1.pk]),
            updated_item
            )

# HERE IS WHERE THE FAILURE IS!!!
    self.assertResponseStatusCodeEqual(response,302)

    self.item1 = self.itemType.objects.get(pk=self.item1.pk)
    self.assertEqual(self.item1.name,updated_name)

但有时(并且间歇性地),当我运行测试套件时,我 post 到此表单以测试保存内容,我收到此错误:

======================================================================
FAIL: test_editor_change_item (aristotle_mdr.tests.test_extension_api.QuestionAdmin)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/travis/build/aristotle-mdr/aristotle-metadata-registry/aristotle_mdr/tests/test_admin_pages.py", line 285, in test_editor_change_item
    self.assertResponseStatusCodeEqual(response,302)
  File "/home/travis/build/aristotle-mdr/aristotle-metadata-registry/aristotle_mdr/tests/utils.py", line 501, in assertResponseStatusCodeEqual
    self.assertEqual(response.status_code, code)
AssertionError: 200 != 302

由于这种性质,如果发布成功,页面应该重定向,如果不是这种情况,我有一些代码只会吐出响应 HTML,在那些情况下我明白了:

<label class="required" for="id_workgroup">Workgroup</label>
<select id="id_workgroup" name="workgroup">
    <option value="">---------</option>
    <option value="17" selected="selected">Test WG 1</option>
</select>
<ul class="errorlist">
    <li>workgroup instance with pk 17 does not exist.</li>
</ul>

但是,当此错误触发时,并非每种项目类型都会引发错误,只有一两个。但是,如果您查看select 字段,就会发现带有id(或pk17 的工作组就在那里!另外,当我重新运行测试套件时,它会很好(有时在一些“热身”之后)。我也从未在非测试站点遇到过这种情况。

我认为这可能是由于 Django 测试保存在事务中的方式?我开始对此感到恼火,因为它曾经很断断续续,但现在它变得越来越频繁 - 但仍然是随机的。


所以这仍然失败,我可以说没有修复它:

  • 使用基于文件的 SQLite 实例而不是内存中
  • 使用 PostgreSQL 进行测试
  • 从 TestCase 切换到 TransactionTestCase

我知道的:

  • 测试在开发服务上运行良好,但相同的测试在 Travis-CI 上失败
  • 不仅是对测试 Web 客户端的调用,而且其他一些查询集也可以工作
  • 它可能是基于事务的,但我不确定。

对于超级好奇的here is the issue I'm trying to quash but can't


编辑:2015-06-11

构建了一个失败的、独立的例子!! SQLite 始终有效,Postgres 始终失败。

似乎出于某种原因,这段代码一直很糟糕:

    def test_bar(self):
        # This test will always work
        print("Do Bar")
        self.do_foo()
        print("Bar done")
    def test_foo(self):
        # This test will always work
        print("Do Foo")
        self.login_editor()
        response = self.client.get(reverse("admin:%s_%s_changelist"%(self.itemType._meta.app_label,self.itemType._meta.model_name)))
        self.assertResponseStatusCodeEqual(response,200)
        self.do_foo()
        print("Foo done")
    def test_zip(self):
        # This test will always FAIL
        print("Do Zip")
        self.do_foo()
        print("Zip done")

事实上,调用管理员changelist 视图总是会导致任何后续管理页面在 Postgres 上失败,因为 Workgroup 不再出现在查询集中。 现在,这是为什么呢?

完整代码:

class MinimalExample(TestCase):
    itemType=models.ObjectClass
    form_defaults = {}
    create_defaults = {}
    def setUp(self):
        self.wg1 = models.Workgroup.objects.create(name="Test WG")

        self.editor = User.objects.create_user('eddie','','editor')
        self.editor.is_staff=True
        self.editor.save()

        self.wg1.submitters.add(self.editor)

        self.assertEqual(self.editor.profile.editable_workgroups.count(),1)
        self.item1 = self.itemType.objects.create(name="admin_page_test_oc",description=" ",workgroup=self.wg1,**self.create_defaults)
    def logout(self):
        self.client.post(reverse('django.contrib.auth.views.logout'), {})

    def login_editor(self):
        self.logout()
        response = self.client.post(reverse('friendly_login'), {'username': 'eddie', 'password': 'editor'})
        self.assertEqual(response.status_code,302)
        return response
    def assertResponseStatusCodeEqual(self,response,code):
        self.assertEqual(response.status_code, code)

    def test_bar(self):
        print("Do Bar")
        self.do_foo()
        print("Bar done")
    def test_foo(self):
        print("Do Foo")
        self.login_editor()
        response = self.client.get(reverse("admin:%s_%s_changelist"%(self.itemType._meta.app_label,self.itemType._meta.model_name)))
        self.assertResponseStatusCodeEqual(response,200)
        self.do_foo()
        print("Foo done")
    def test_zip(self):
        print("Do Zip")
        self.do_foo()
        print("Zip done")
    def do_foo(self):
        url_bits = (self.itemType._meta.app_label,self.itemType._meta.model_name)
        response = self.client.post(reverse('friendly_login'), {'username': 'eddie', 'password': 'editor'})

        response = self.client.get(reverse("admin:%s_%s_add"%url_bits))

        data = {'name':"admin_page_test_oc",'description':"test","workgroup":self.wg1.id,
                    'statuses-TOTAL_FORMS': 0, 'statuses-INITIAL_FORMS': 0 #no substatuses
                }
        response = self.client.post(reverse("admin:%s_%s_add"%url_bits),data)
        self.item1 = self.itemType.objects.first()
        response = self.client.get(reverse("admin:%s_%s_change"%url_bits,args=[self.item1.id]))

        data['name'] = "updated"
        # Re post the same data
        response = self.client.post(
                reverse("admin:%s_%s_change"%url_bits,args=[self.item1.id]),
                data
                )
        print response
        self.item1 = self.itemType.objects.first() # decache
        self.assertTrue(self.item1.name == "updated")

【问题讨论】:

  • 你能显示有这个错误的实际测试吗?
  • @knbk 完成并完成。
  • 如果您单独运行这个特定的测试方法(在删除所有 *.pyc 文件之后),是否也会发生这种情况?您的视图可能正在重用相同的表单实例,因此查询集设置一次并且永远不会更改。当然,如果您也向我们展示您的视图代码,这将更容易排除。
  • @RobertJørgensgaardEngdahl 这主要发生在 Travis-CI 测试实例上,它每次都在新环境中这样做,因此它不是陈旧的 *.pyc 文件。没有“视图”作为其管理页面,但我可以将您链接到完整代码。查询集的想法很好,但是您可以看到该选项在 HTML 中可见,因此存在一些东西。
  • 没有冒犯。如果我是你,我实际上会去阅读 Django 管理代码,看看是什么导致了这种情况。作者可能没有考虑到您的特定设置。 Django 是最好的,但它不是没有错误的。

标签: python django django-testing django-tests


【解决方案1】:

我会先在您加载该工作组的位置移动。尝试将工作组创建移出setUp 并移入setUpClass。这具有为 TestCase 中的所有测试保留工作组的效果,这可能是您想要的。

def setUp(self):
    from django.test import Client
    self.client = Client()

@classmethod
def setUpClass(cls):
    cls.wg1 = models.Workgroup.objects.create(name="Test WG 1") # Editor is member
    super().setUpClass() # Python 3 version

夹具通常是一场噩梦,但如果这没有帮助,我有兴趣看看是否将该工作组移至夹具并查看问题是否消失。

【讨论】:

  • 这可能不起作用,因为在操作过程中会删除某些项目,但我认为工作组并非如此。我可能只是试一试!我会告诉你的。
  • 只有在运行完整的测试套件时才会出现问题?如果是这样,您可能有另一个干扰它的测试。也许检查你的其他测试,如果他们删除了一些东西,把它放回一个 tearDown 方法。
【解决方案2】:

哇,多么狂野而疯狂的骑行!

事实证明,这是RelatedListFilter 中的一个问题,自定义查询集导致了问题。 The comments on this answer指出:

过滤器查找以某种方式被缓存

field 和 field.rel 对象将在请求之间持续存在

在某些情况下,甚至跨事务回滚似乎!

故事的寓意,在管理员中使用ListFilters时要小心!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-12-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-21
    相关资源
    最近更新 更多