web-dev-qa-db-ja.com

djangoでファイルのアップロードを単体テストする方法

私のDjangoアプリでは、ファイルのアップロードを実現するビューがあります。コアスニペットはこのようです

...
if  (request.method == 'POST'):
    if request.FILES.has_key('file'):
        file = request.FILES['file']
        with open(settings.destfolder+'/%s' % file.name, 'wb+') as dest:
            for chunk in file.chunks():
                dest.write(chunk)

ビューを単体テストしたいと思います。幸せなパスと失敗したパスをテストする予定です。つまり、request.FILESにはキー 'file'がありません。request.FILES['file']にはNone。があります。

ハッピーパスの投稿データを設定するにはどうすればよいですか?

83
damon

From Django docs on Client.post

ファイルの送信は特別なケースです。ファイルにPOSTを指定するには、ファイルフィールド名をキーとして指定し、値としてアップロードするファイルのファイルハンドルを指定するだけです。例:

c = Client()
with open('wishlist.doc') as fp:
  c.post('/customers/wishes/', {'name': 'fred', 'attachment': fp})
97
Arthur Neves

以前は同じwith open('some_file.txt') as fp:を使用していましたが、リポジトリに画像、ビデオ、その他の実際のファイルが必要でした。また、Djangoコアコンポーネントよくテストされているので、現在これは私がやっていることです:

from Django.core.files.uploadedfile import SimpleUploadedFile

def test_upload_video(self):
    video = SimpleUploadedFile("file.mp4", "file_content", content_type="video/mp4")
    self.client.post(reverse('app:some_view'), {'video': video})
    # some important assertions ...

Python 3.5 +では、bytesの代わりにstrオブジェクトを使用する必要があります。変化する "file_content"からb"file_content"

SimpleUploadedFileは通常のアップロードのように動作するInMemoryFileを作成し、名前、コンテンツ、コンテンツタイプを選択できます。

87
Danilo Cabello

Django RequestFactory 。をご覧になることをお勧めします。これは、リクエストで提供されるデータをモックする最良の方法です。

そうは言っても、私はあなたのコードにいくつかの欠陥を見つけました。

  • 「ユニット」テストとは、テストすることを意味しますjust one機能の「ユニット」。そのため、そのビューをテストする場合は、ビューをテストすることになり、ファイルシステム、つまり、ユニットテストではありません。この点をより明確にするため。そのテストを実行し、ビューは正常に機能するが、そのファイルを保存する権限がない場合、そのためテストは失敗します。
  • 他の重要なことは、テスト速度です。 TDDのようなことをしている場合、テストの実行速度は本当に重要です。 I/Oにアクセスするのは得策ではありません

したがって、refactorのようなビューを使用して、次のような関数を使用することをお勧めします。

def upload_file_to_location(request, location=None): # Can use the default configured

そして、そのことをあざけります。 Python Mock を使用できます。

PS:Django Test Client を使用することもできますが、それは、クライアントがセッション、ミドルウェア、など。単体テストに似たものはありません。

6
santiagobasulto

私は自分のイベント関連のアプリケーションのためにこのようなことをしますが、あなたはあなた自身のユースケースに取り組むために十分以上のコードを持っている必要があります

import tempfile, csv, os

class UploadPaperTest(TestCase):

    def generate_file(self):
        try:
            myfile = open('test.csv', 'wb')
            wr = csv.writer(myfile)
            wr.writerow(('Paper ID','Paper Title', 'Authors'))
            wr.writerow(('1','Title1', 'Author1'))
            wr.writerow(('2','Title2', 'Author2'))
            wr.writerow(('3','Title3', 'Author3'))
        finally:
            myfile.close()

        return myfile

    def setUp(self):
        self.user = create_fuser()
        self.profile = ProfileFactory(user=self.user)
        self.event = EventFactory()
        self.client = Client()
        self.module = ModuleFactory()
        self.event_module = EventModule.objects.get_or_create(event=self.event,
                module=self.module)[0]
        add_to_admin(self.event, self.user)

    def test_paper_upload(self):
        response = self.client.login(username=self.user.email, password='foz')
        self.assertTrue(response)

        myfile = self.generate_file()
        file_path = myfile.name
        f = open(file_path, "r")

        url = reverse('registration_upload_papers', args=[self.event.slug])

        # post wrong data type
        post_data = {'uploaded_file': i}
        response = self.client.post(url, post_data)
        self.assertContains(response, 'File type is not supported.')

        post_data['uploaded_file'] = f
        response = self.client.post(url, post_data)

        import_file = SubmissionImportFile.objects.all()[0]
        self.assertEqual(SubmissionImportFile.objects.all().count(), 1)
        #self.assertEqual(import_file.uploaded_file.name, 'files/registration/{0}'.format(file_path))

        os.remove(myfile.name)
        file_path = import_file.uploaded_file.path
        os.remove(file_path)
4
super9

私はそのようなことをしました:

from Django.core.files.uploadedfile import SimpleUploadedFile
from Django.test import TestCase
from Django.core.urlresolvers import reverse
from Django.core.files import File
from Django.utils.six import BytesIO

from .forms import UploadImageForm

from PIL import Image
from io import StringIO


def create_image(storage, filename, size=(100, 100), image_mode='RGB', image_format='PNG'):
   """
   Generate a test image, returning the filename that it was saved as.

   If ``storage`` is ``None``, the BytesIO containing the image data
   will be passed instead.
   """
   data = BytesIO()
   Image.new(image_mode, size).save(data, image_format)
   data.seek(0)
   if not storage:
       return data
   image_file = ContentFile(data.read())
   return storage.save(filename, image_file)


class UploadImageTests(TestCase):
   def setUp(self):
       super(UploadImageTests, self).setUp()


   def test_valid_form(self):
       '''
       valid post data should redirect
       The expected behavior is to show the image
       '''
       url = reverse('image')
       avatar = create_image(None, 'avatar.png')
       avatar_file = SimpleUploadedFile('front.png', avatar.getvalue())
       data = {'image': avatar_file}
       response = self.client.post(url, data, follow=True)
       image_src = response.context.get('image_src')

       self.assertEquals(response.status_code, 200)
       self.assertTrue(image_src)
       self.assertTemplateUsed('content_upload/result_image.html')

create_image関数は画像を作成するので、画像の静的パスを指定する必要はありません。

注:コードごとにコードを更新できます。 Python 3.6。

3
Chirag Maliwal

Django 1.7では、TestCaseに問題がありますが、open(filepath、 'rb')を使用して解決できますが、テストクライアントを使用する場合、それを制御することはできません。 file.read()が常にバイトを返すようにします。

ソース: https://code.djangoproject.com/ticket/23912 、KevinEtienne

Rbオプションがないと、TypeErrorが発生します:

TypeError: sequence item 4: expected bytes, bytearray, or an object with the buffer interface, str found
1
Rômulo Collopy
from rest_framework.test import force_authenticate
from rest_framework.test import APIRequestFactory

factory = APIRequestFactory()
user = User.objects.get(username='#####')
view = <your_view_name>.as_view()
with open('<file_name>.pdf', 'rb') as fp:
    request=factory.post('<url_path>',{'file_name':fp})
force_authenticate(request, user)
response = view(request)
0
Suvodeep Dubey