概要
Djangoでファイルアップロードのあるフォームをユニットテストしようとして、解決にやや時間がかかったので忘備録として残しておきます。
悩んだところ
- ファイルインプットの箇所をどうやってテストするか
- 予め用意したテスト用ファイルでテストするにはどうすれば良いか
ファイルアップロードのあるフォームについて
- フォームのフィールドとしては、以下のクラスのフィールドであれば対応したファイルを受け付けるフィールドになる
-
django.forms.fields.FileField
:ファイル全般 -
django.forms.fields.ImageField
:画像ファイル - 参考:フォームAPI:Binding uploaded files to a form
- テンプレートへの描画については、
widget
キーワードでdjango.forms.widgets.FileInput
を指定するとファイルアップロードのinputタグを描画する - ファイルアップロードのあるフォームを使う場合は、テンプレートの
form
タグの属性にenctype="multipart/form-data"
を指定する必要がある - 参考:ファイルのアップロード:ファイルのアップロードの基本
ユニットテスト
結論
- テストするフォームのインスタンス生成時に、以下のものを渡せば良い
- 第1引数:ファイル以外のフィールドのdict
- 第2引数:ファイルフィールドのdict
- テキストファイルのテストならば、dictの値は
django.core.files.uploadedfile.SimpleUploadedFile
を使うと良い- 第1引数:ファイル名
- 第2引数:ファイルの内容(bytes型)
- 第3引数:コンテンツタイプ(デフォルトでは
text/plain
なので省略可)
- テキストファイルのテストならば、dictの値は
予め用意したテストファイルでファイルフィールド付きフォームのユニットテストをしたい場合
テスト対象のフォーム
forms.py
from django import forms
class FileUploadForm(forms.Form):
name = forms.fields.CharField(
label='名前'
max_length=50,
required=True
)
file = forms.fields.FileField(
label='ファイル',
required=True,
widget=forms.widgets.FileInput
)
ユニットテスト
- テストファイルを
open
メソッドで開き、ファイルハンドルを取得する
-
with
句を使うと閉じ忘れが防げる
- 1.で開いたファイルハンドルの
read
メソッドでファイル内容を読み出す - 2.で読みだしたファイル内容をbytes型に変換する
- 3.で変換した内容を
SimpleUploadedFile
のファイル内容として指定する - 4.で生成した
SimpleUploadedFile
のインスタンスをファイルデータのdictの値に設定する - 5.で生成したファイルデータをテスト対象のフォームのコンストラクタの第2引数として渡す
tests.py
from django.test.testcases import TestCase
from django.core.files.uploadedfile import SimpleUploadedFile
from .forms import FileUploadForm
class FileUploadFormTest(TestCase):
def test_fileupload(self):
# テスト用ファイルを読み込んでアップロードファイルのデータを作成する
# 同一ディレクトリにテスト用ファイルを配置する前提
# 1. テストファイルをopenメソッドで開き、ファイルハンドルを取得する
with open('test.txt') as f:
# SimpleFileUploadのインスタンスを生成する
# 2. 開いたファイルのreadメソッドでファイル内容を読み出す
# 3. 読みだしたファイル内容をbytes型に変換する
uploadedFile = SimpleUplodedFile(f.name, bytes(f.read(). encording=f.encording))
# アップロードファイルのデータを作成する
# 4. SimpleUploadedFileのインスタンスをファイルデータのdictに設定する
files = {'file': uploadedFile }
# ファイル以外のフィールドのデータを作成する
data = {'name': 'ユーザー'}
# フォームのコンストラクタにテストデータを渡す
# 第1引数:ファイル以外のフィールドのデータ
# 第2引数:ファイルフィールドのデータ
form = FileUploadForm(data, files)
self.assertTrue(form.is_valid())
Djangoでのファイルアップロードについてあれこれ
- Viewのメソッドが呼び出されたとき、アップロードされたファイルのデータは
request.FILE
に入る - Viewのメソッドでリクエストからフォームクラスのインスタンスを生成するとき、コンストラクタへ以下のものを渡すことが出来る
- 第1引数:
request.POST
(Web画面からPOSTされたテキストやパスワード等のフィールドデータ) - 第2引数:
request.FILE
(Web画面からPOSTされたファイルアップロードのデータ) - リクエストを受け付けたときのファイルアップロードのデータは、Djangoのデフォルトでは以下のいずれかになっている
-
django.core.files.uploadedfile.InMemoryUploadedFile
:ファイルサイズが2.5MB未満 -
django.core.files.uploadedfile.TemporaryUploadedFile
:ファイルサイズが2.5MB以上 - 参考:ファイルのアップロード:アップロードハンドラ