it-roy-ru.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 ..

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

72
damon

Из документов Django на Client.post :

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

c = Client()
with open('wishlist.doc') as fp:
  c.post('/customers/wishes/', {'name': 'fred', 'attachment': fp})
94
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, который ведет себя как обычная загрузка, и вы можете выбрать имя, контент и тип контента.

68
Danilo Cabello

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

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

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

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

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

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

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

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

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

0
Chirag Maliwal