如何在django中单元测试文件上传

dam*_*mon 85 django unit-testing file-upload

在我的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)
Run Code Online (Sandbox Code Playgroud)

我想对视图进行单元测试.我打算测试快乐路径以及失败路径.. request.FILES在没有密钥'文件'的情况下,情况哪里request.FILES['file']None...

如何为快乐路径设置发布数据?有人能告诉我吗?

Art*_*ves 98

来自Django的文档Client.post:

提交文件是一种特殊情况.要POST文件,您只需要提供文件字段名称作为键,并将文件句柄提供给您希望作为值上载的文件.例如:

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

  • 链接到相关的Django doc:https://docs.djangoproject.com/en/dev/topics/testing/overview/#django.test.client.Client.post (8认同)
  • 恕我直言使用客户端它不再是单元测试;) (5认同)
  • 死链接,请参阅https://docs.djangoproject.com/en/1.7/topics/testing/tools/#django.test.Client.post (3认同)
  • Henning在技术上是正确的-这更多的是“集成测试”-直到您接触到更复杂的代码库才真正重要,甚至即使有真正的测试团队也是如此 (2认同)
  • 相关 Django 文档的链接更新:https://docs.djangoproject.com/en/dev/topics/testing/tools/#django.test.Client.post (2认同)

Dan*_*llo 94

我曾经做同样的事情,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 ...
Run Code Online (Sandbox Code Playgroud)

Python 3.5+中,您需要使用bytesobject而不是str.更改"file_content"b"file_content"

它工作正常,SimpleUploadedFile创建的InMemoryFile行为类似于常规上传,您可以选择名称,内容和内容类型.

  • 这应该被认为是这个问题的正确答案。谢谢@DaniloCabello。 (2认同)
  • 您可以使用 base64.b64decode("iVBORw0KGgoAAAANSUhEUgAAAAUA" + "AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO" + "9TXL0Y4OHwAAAABJRU5ErkJggg==") 作为图像内容,这会创建真实图像。 (2认同)
  • 我必须使用 `bytes` 而不是 `str`:`b"file_content"`。 (2认同)

san*_*lto 6

我建议你看一下Django RequestFactory.IT是模拟请求中提供的数据的最佳方式.

说,我在你的代码中发现了一些缺陷.

  • "单元"测试意味着只测试一个功能单元"单元".所以,如果你想测试那个视图,你要测试视图,文件系统,ergo,不是真正的单元测试.为了更清楚地说明这一点.如果您运行该测试,并且视图工作正常,但您没有权限保存该文件,那么您的测试将失败.
  • 其他重要的是测试速度.如果您正在执行类似TDD的操作,那么执行测试的速度非常重要. 访问任何I/O都不是一个好主意.

因此,我建议您重构视图以使用如下函数:

def upload_file_to_location(request, location=None): # Can use the default configured
Run Code Online (Sandbox Code Playgroud)

并做一些嘲弄.你可以使用Python Mock.

PS:你也可以使用Django 测试客户端但这意味着你要添加更多的东西来测试,因为客户端使用Sessions,middleweres等.没有类似于单元测试.

  • 嘿@Dmitry.模拟是去那里的方式.每当您必须访问外部资源时,您应该模拟它.假设你有一个名为`profile_picture`的视图,它在内部使用`upload_profile_picture`函数.如果要测试该视图,只需模拟内部函数并确保在测试时调用它.这是一个简单的例子:https://gist.github.com/santiagobasulto/6437356 (5认同)

sup*_*er9 5

我为我自己的事件相关应用程序做了这样的事情,但你应该有足够多的代码来处理你自己的用例

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)
Run Code Online (Sandbox Code Playgroud)


Chi*_*wal 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')
Run Code Online (Sandbox Code Playgroud)

create_image 函数将创建图像,因此您无需提供图像的静态路径。

注意:您可以根据您的代码更新代码。此代码适用于 Python 3.6。