[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)

(가) path..ie는이 경우 실패로 나뿐만 아니라 행복한 경로를 테스트하는 단위 테스트에 view.I 오전 계획을하고 싶은 request.FILES키와 ‘파일을’이없는, 경우 request.FILES['file']이를 None

행복한 길에 대한 게시물 데이터를 어떻게 설정합니까? 누군가 말해 줄 수 있습니까?



답변

장고 문서에서 Client.post:

파일 제출은 특별한 경우입니다. 파일을 게시하려면 파일 필드 이름을 키로 제공하고 업로드하려는 파일에 대한 파일 핸들을 값으로 제공하면됩니다. 예를 들면 :

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


답변

나는 똑같은 일을 with open('some_file.txt') as fp:했지만 repo에 이미지, 비디오 및 기타 실제 파일이 필요했으며 잘 테스트 된 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 ...

에서 파이썬 3.5 당신은 사용할 필요가 bytes대신 개체를 str. 변경 "file_content"b"file_content"

정상적으로 작동하고 있으며 일반 업로드처럼 작동 SimpleUploadedFile하는 파일 InMemoryFile을 만들고 이름, 콘텐츠 및 콘텐츠 유형을 선택할 수 있습니다.


답변

Django RequestFactory를 살펴 보는 것이 좋습니다 . 요청에 제공된 데이터를 모의하는 가장 좋은 방법입니다.

코드에서 몇 가지 결함을 발견했습니다.

  • “단위”테스트 는 기능의 “단위” 하나만 테스트하는 것을 의미 합니다. 따라서 해당 뷰를 테스트하려면 실제로 단위 테스트가 아닌 뷰와 파일 시스템을 테스트해야합니다. 이 점을 더 명확하게하기 위해. 해당 테스트를 실행하고보기가 제대로 작동하지만 해당 파일을 저장할 권한이없는 경우 테스트가 실패합니다.
  • 다른 중요한 것은 테스트 속도 입니다. TDD와 같은 작업을 수행하는 경우 테스트 실행 속도가 정말 중요합니다.
    I / O에 액세스하는 것은 좋은 생각이 아닙니다 .

따라서 다음과 같은 기능을 사용하도록 뷰를 리팩터링하는 것이 좋습니다 .

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

그리고 그것에 대해 약간의 조롱을하십시오. Python Mock을 사용할 수 있습니다 .

추신 : 장고 테스트 클라이언트를 사용할 수도 있습니다. 하지만 그 클라이언트가 세션, 미들웨어 등을 사용하기 때문에 테스트 할 다른 것을 더 추가한다는 의미입니다. 단위 테스트와 비슷한 것은 없습니다.


답변

내 자신의 이벤트 관련 응용 프로그램에 대해 이와 같은 작업을 수행하지만 자신의 사용 사례를 수행하기에 충분한 코드가 있어야합니다.

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)


답변

나는 다음과 같이했다.

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 용 코드입니다.


답변

Django 1.7에는 open (filepath, ‘rb’)를 사용하여 해결할 수있는 TestCase에 문제가 있지만 테스트 클라이언트를 사용할 때는 제어 할 수 없습니다. file.read ()가 항상 바이트를 반환하는지 확인하는 것이 가장 좋습니다.

출처 : https://code.djangoproject.com/ticket/23912 , by KevinEtienne

rb 옵션이 없으면 TypeError가 발생합니다.

TypeError: sequence item 4: expected bytes, bytearray, or an object with the buffer interface, str found


답변

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)