Подтвердить что ты не робот

Как unit test загрузка файла в 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)

Я хотел бы unit test view.I планирую протестировать счастливый путь, а также путь fail..ie, случай, когда request.FILES не имеет ключевого "файла", случай, когда request.FILES['file'] имеет None..

Как настроить почтовые данные для счастливого пути? Может ли кто-нибудь сказать мне?

4b9b3361

Ответ 1

Из Django docs на Client.post:

Отправка файлов - это особый случай. Для POST файла вам нужно только укажите имя файла в качестве ключа и дескриптор файла в файл вы хотите загрузить в качестве значения. Например:

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

Ответ 2

Я использовал один и тот же 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, который ведет себя как обычная загрузка, и вы можете выбрать имя, содержимое и тип контента.

Ответ 3

Я рекомендую вам взглянуть на Django RequestFactory. Это лучший способ для макета данных, указанных в запросе.

Сказал, что я нашел несколько недостатков в вашем коде.

  • "юнит" тестирование означает тестирование только одного "юнита" функциональности. Итак, если вы хотите протестировать это представление, вы будете тестировать это представление, а файловая система, а значит, не совсем модульное тестирование. Чтобы сделать этот момент более ясным. Если вы запустите этот тест, и представление будет работать нормально, но у вас нет прав для сохранения этого файла, ваш тест не пройдёт из-за этого.
  • Другая важная вещь - это скорость теста. Если вы делаете что-то вроде TDD, скорость выполнения ваших тестов действительно важна. Доступ к любому вводу/выводу не очень хорошая идея.

Итак, я рекомендую вам реорганизовать ваше представление, чтобы использовать такую функцию, как:

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

И посмеяться над этим. Вы можете использовать Python Mock.

PS: Вы также можете использовать Django Test Client, но это будет означать, что вы добавляете еще одну вещь для тестирования, потому что этот клиент использует Sessions, middleware и т.д. Ничего похожего на модульное тестирование.

Ответ 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)

Ответ 5

Я сделал что-то подобное:

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.

Ответ 6

В Django 1.7 существует проблема с TestCase, которая может быть разрешена с помощью open (путь к файлу, "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

Ответ 7

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)