ユーザーがForeignKeyを特定の値に変更できるかどうかを確認するDjangoアプリを非常にカスタマイズしました。
この場合、 aは aUser
に属しWorkgroup
、 anItem
もaWorkgroup
に属することができるため、 aUser
が anを作成するとき、それらは自分が属する s にItem
のみ入れることができます。Workgroup
ややこしい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>
ただし、このエラーが発生した場合、すべての項目タイプがエラーをスローするわけではなく、1 つまたは 2 つのみです。select
しかし、フィールドを見ると、id
(またはpk
)のワークグループ17
が存在します。さらに、テスト スイートを再実行すると問題なく実行されます (「ウォームアップ」が数回行われる場合があります)。また、テスト以外のサイトでこれに遭遇したことはありません。
これは、Django テストがトランザクションに保持される方法が原因であると思いますか? 以前は非常に断続的でしたが、今ではより頻繁になっていますが、それでもランダムであるため、私はこれに悩まされ始めています。
したがって、これはまだ失敗しており、何が修正されていないかを言うことができます:
- インメモリの代わりにファイルベースの SQLite インスタンスを使用する
- テストに PostgreSQL を使用する
- TestCase から TransactionTestCase への切り替え
私が知っていること:
- 開発サーバーではテストは正常に実行されますが、Travis-CI では同じテストが失敗します
- テスト Web クライアントへの呼び出しだけでなく、他のいくつかのクエリセットも機能します
- トランザクションベースかもしれませんが、よくわかりません。
そして、非常に好奇心旺盛な人のために、ここに私が鎮圧しようとしているができない問題があります。
編集: 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
Workgroup
クエリセットに表示されなくなったため、保存しようとすると、Postgres で後続の管理ページが常に失敗します。さて、それはなぜですか?
完全なコード:
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")