장고에서 단위 테스트 파일 업로드하는 방법


99

내 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...

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


클라이언트 클래스를 사용하여 답을 올바르게 표시
Henning

답변:


109

장고 문서에서 Client.post:

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

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



2
헤닝은 기술적으로 정확 - 이것은 더의 것 integration test-하지 않습니다 실제로 문제가 당신은 어쩌면 실제 테스트 팀에보다 복잡한 코드베이스로 얻을 때까지
앨빈

웹 프레임 워크에서는 뷰를 테스트하는 경우 차이가 훨씬 적습니다. 클라이언트를 통해 응답을받는 것과 함수에서 직접받는 것은 대부분의 테스트가 유효하기에 충분히 유사합니다. 또한 클라이언트는 더 많은 유연성을 제공합니다. 그것이 제가 개인적으로 사용하는 것입니다.
trpt4him

관련 장고 문서에 대한 링크 업데이트 : docs.djangoproject.com/en/dev/topics/testing/tools/...
얼어 붙었다

109

나는 똑같은 일을 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을 만들고 이름, 콘텐츠 및 콘텐츠 유형을 선택할 수 있습니다.


1
귀하의 예를 사용하여 양식 유효성 검사에서 "유효한 이미지를 업로드하십시오. 업로드 한 파일이 이미지가 아니거나 손상된 이미지였습니다."
antonagestam

@antonagestam 올바른 콘텐츠 유형을 전달하고 있습니까? 양식이 파일의 내용을 확인하고 있습니까? "file_content"유효한 이미지 헤더가 필요한 경우 코드에서 유효한 이미지라고 생각합니다.
Danilo Cabello 2015 년

JPEG 및 PNG에 적합한 헤더는 무엇입니까?
antonagestam

2
이 문제에 대한 정답으로 간주되어야합니다. @DaniloCabello 감사합니다.
mannysz

1
base64.b64decode ( "iVBORw0KGgoAAAANSUhEUgAAAAUA"+ "AAAFCAYAAACNbyblAAAAHElEQVQI12P4 // 8 / w38GIAXDIBKE0DHxgljNBAAO"+ "9TXL0Y4OHwAAAABJRU5ErkJggg ==") 이미지 콘텐츠로 사용할 수 있습니다. 이것은 실제 이미지를 생성합니다.
Howdedo

6

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

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

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

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

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

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

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


1
내가 틀릴 수도 있지만 통합 테스트를 의미하고 '단위 테스트'라는 용어를 잘못 사용한 것 같습니다.
jooks apr

1
@santiagobasulto 저는 TDD의 초보자이고 단위 테스트 속도를 높이고 싶습니다. 하지만 단위 테스트 중에 원격 스토리지 (Amazon S3)에 파일을 업로드하는 파일 업로드를 다루는 몇 가지보기가 있습니다. 시간이 걸립니다. 테스트 중에 I / O 액세스를 방지하는 방법을 자세히 보여주기 위해 답변을 확장 해 주시겠습니까?
Dmitry Wojciechowski 2013 년

5
안녕 @Dmitry. 모의는 거기에가는 길이다. 외부 리소스에 액세스해야 할 때마다이를 조롱해야합니다. profile_picture내부적으로 upload_profile_picture함수를 사용하는 뷰가 있다고 가정합니다 . 해당 뷰를 테스트하려면 내부 함수를 조롱하고 테스트에서 호출되는지 확인하십시오. 다음은 간단한 예입니다. gist.github.com/santiagobasulto/6437356
santiagobasulto

4

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

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

나는 다음과 같이했다.

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


1

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

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

APIRequestFactory를 활용하는 유일한 답변
majkelx

0

Django의 공식 문서 에서 언급했듯이 :

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

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

추가 정보 : 파일이 일부 함수에 인수로 전달되었는지 확인하는 방법은 무엇입니까?

테스트하는 동안 때때로 파일이 일부 함수에 대한 인수로 전달되는지 확인하고 싶습니다.

예 :

...
class AnyView(CreateView):
    ...
    def post(self, request, *args, **kwargs):
        attachment = request.FILES['attachment']
        # pass the file as an argument
        my_function(attachment)
        ...

테스트에서 다음과 같이 Python의 mock을 사용 합니다.

# Mock 'my_function' and then check the following:

response = do_a_post_request()

self.assertEqual(mock_my_function.call_count, 1)
self.assertEqual(
    mock_my_function.call_args,
    call(response.wsgi_request.FILES['attachment']),
)

0
from django.test import Client
from requests import Response

client = Client()
with open(template_path, 'rb') as f:
    file = SimpleUploadedFile('Name of the django file', f.read())
    response: Response = client.post(url, format='multipart', data={'file': file})

도움이 되었기를 바랍니다.


0

Python == 3.8.2, Django == 3.0.4, djangorestframework == 3.11.0을 사용하고 있습니다.

시도 self.client.post했지만 Resolver404예외 가 발생했습니다.

다음은 나를 위해 일했습니다.

import requests
upload_url='www.some.com/oaisjdoasjd' # your url to upload
with open('/home/xyz/video1.webm', 'rb') as video_file:
    # if it was a text file we would perhaps do
    # file = video_file.read()
    response_upload = requests.put(
        upload_url,
        data=video_file,
        headers={'content-type': 'video/webm'}
    )
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.