Django 测试中的异常错误错误地认为实例不存在
Unusual error in Django tests wrongly thinking instance doesn't exist
我有一个非常定制的 Django 应用程序,它检查用户是否可以将外键更改为特定值。
在这种情况下,User
属于 Workgroup
,而 Item
也可以属于 Workgroup
,因此,当 User
做一个Item
,他们只能放在自己所属的Workgroup
里。复杂的事情Item
是父class,所以"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
(或 pk
)17
的工作组就在那里!另外,当我重新 运行 测试套件时,它会很好(有时在一些 "warm-up" 之后)。我也从未在非测试站点遇到过这种情况。
我认为这可能是由于 Django 测试在事务中的保存方式所致?我开始对此感到恼火,因为它以前是断断续续的,但现在越来越频繁了——但仍然是随机的。
所以这仍然失败,我可以说 没有 修复它:
- 使用基于文件的 SQLite 实例而不是内存中的实例
- 使用 PostgreSQL 进行测试
- 从 TestCase 切换到 TransactionTestCase
我知道的:
- 在开发服务上测试 运行 正常,但在 Travis 上相同的测试失败-CI
- 它不仅可以调用测试网络客户端,还可以调用其他一些查询集
- 它可能是基于交易的,但我不确定。
对于极度好奇的人 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")
我首先会在加载该工作组的位置四处移动。尝试将工作组创建移出 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
夹具通常是一场噩梦,但如果这没有帮助,我很想看看是否将该工作组移动到夹具并查看问题是否消失。
哇,多么疯狂的旅程!!
原来是 RelatedListFilter
中的一个问题导致了自定义查询集的问题。 The comments on this answer 指出:
The filter lookups are getting cached in some way
和
field and field.rel objects will persist between requests
甚至在某些情况下似乎跨事务回滚!
故事的寓意,在管理员中使用ListFilters
时要小心!
我有一个非常定制的 Django 应用程序,它检查用户是否可以将外键更改为特定值。
在这种情况下,User
属于 Workgroup
,而 Item
也可以属于 Workgroup
,因此,当 User
做一个Item
,他们只能放在自己所属的Workgroup
里。复杂的事情Item
是父class,所以"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
(或 pk
)17
的工作组就在那里!另外,当我重新 运行 测试套件时,它会很好(有时在一些 "warm-up" 之后)。我也从未在非测试站点遇到过这种情况。
我认为这可能是由于 Django 测试在事务中的保存方式所致?我开始对此感到恼火,因为它以前是断断续续的,但现在越来越频繁了——但仍然是随机的。
所以这仍然失败,我可以说 没有 修复它:
- 使用基于文件的 SQLite 实例而不是内存中的实例
- 使用 PostgreSQL 进行测试
- 从 TestCase 切换到 TransactionTestCase
我知道的:
- 在开发服务上测试 运行 正常,但在 Travis 上相同的测试失败-CI
- 它不仅可以调用测试网络客户端,还可以调用其他一些查询集
- 它可能是基于交易的,但我不确定。
对于极度好奇的人 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")
我首先会在加载该工作组的位置四处移动。尝试将工作组创建移出 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
夹具通常是一场噩梦,但如果这没有帮助,我很想看看是否将该工作组移动到夹具并查看问题是否消失。
哇,多么疯狂的旅程!!
原来是 RelatedListFilter
中的一个问题导致了自定义查询集的问题。 The comments on this answer 指出:
The filter lookups are getting cached in some way
和
field and field.rel objects will persist between requests
甚至在某些情况下似乎跨事务回滚!
故事的寓意,在管理员中使用ListFilters
时要小心!