12
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

【Python】【Django】ファイルアップロードのあるフォームとユニットテスト

Last updated at Posted at 2019-10-27

概要

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なので省略可)

予め用意したテストファイルでファイルフィールド付きフォームのユニットテストをしたい場合

テスト対象のフォーム

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
    )

ユニットテスト

  1. テストファイルをopenメソッドで開き、ファイルハンドルを取得する
  • with句を使うと閉じ忘れが防げる
  1. 1.で開いたファイルハンドルのreadメソッドでファイル内容を読み出す
  2. 2.で読みだしたファイル内容をbytes型に変換する
  3. 3.で変換した内容をSimpleUploadedFileのファイル内容として指定する
  4. 4.で生成したSimpleUploadedFileのインスタンスをファイルデータのdictの値に設定する
  5. 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以上
  • 参考:ファイルのアップロード:アップロードハンドラ
12
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?