как загрузить модульный тестовый файл в 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, тот случай, когда request.FILES не имеет ключа 'file', гдеrequest.FILES['file'] и None..

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

6 66

6 ответов:

из Django docs на 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: но тогда мне нужны были изображения, видео и другие реальные файлы в РЕПО, а также я тестировал часть компонента ядра 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 это ведет себя как обычная загрузка, и вы можете выбрать наименование, содержание и тип контента.

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

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

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

Я делаю что-то вроде этого для моего собственного приложения, связанного с событиями, но у вас должно быть более чем достаточно кода, чтобы справиться с вашим собственным прецедентом

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)

в Django 1.7 есть проблема с TestCase, которая может быть решена с помощью open(filepath, 'rb'), но при использовании тестового клиента мы не контролируем его. Я думаю, что, вероятно, лучше всего обеспечить файл.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 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.