【问题标题】:Unit Testing a Django Form with a FileField使用 FileField 对 Django 表单进行单元测试
【发布时间】:2011-01-29 05:25:07
【问题描述】:

我有这样的表格:

#forms.py
from django import forms

class MyForm(forms.Form):
    title = forms.CharField()
    file = forms.FileField()


#tests.py
from django.test import TestCase
from forms import MyForm

class FormTestCase(TestCase)
    def test_form(self):
        upload_file = open('path/to/file', 'r')
        post_dict = {'title': 'Test Title'}
        file_dict = {} #??????
        form = MyForm(post_dict, file_dict)
        self.assertTrue(form.is_valid())

如何构造 file_dict 以将 upload_file 传递给表单?

【问题讨论】:

    标签: django unit-testing django-forms


    【解决方案1】:

    接受的答案有一个缺点,即您总是必须在您的环境中保留一个虚拟文件进行测试。

    假设您在团队或生产中工作,这不是很好的做法。 @xyres 方法对我来说似乎更干净。不过还可以简化一些。

    正如Python3 io docks 提到的,您可以执行以下操作:

    loaded_file = BytesIO(b"some dummy bcode data: \x00\x01")
    loaded_file.name = 'test_file_name.xls'
    
    # and to load it in form
    file_dict = {'file': SimpleUploadedFile(loaded_file.name, loaded_file.read())}
    

    完整版

    # Python3
    from io import BytesIO
    from django.core.files.uploadedfile import SimpleUploadedFile
    
    class FormTestCase(TestCase)
        def test_form(self):
            # Simple and Clear
            loaded_file = BytesIO(b"some dummy bcode data: \x00\x01")
            loaded_file.name = 'test_file_name.xls'
    
            post_dict = {'title': 'Test Title'}
            file_dict = {'file': SimpleUploadedFile(loaded_file.name, loaded_file.read())}
            form = MyForm(post_dict, file_dict)
            self.assertTrue(form.is_valid())
    

    还建议使用setUpTestData(cls) 和setUp(self) 类方法进行数据准备。 我个人认为Mozilla's intro to unit testing 信息丰富且简单明了。

    【讨论】:

    • 这个答案比公认的答案更干净。
    【解决方案2】:

    使用 Python 3 结合了这篇文章和其他 Stack 文章中的一些想法。不需要外部文件或库。

    from django.core.files.uploadedfile import SimpleUploadedFile
    
    png_hex = ['\x89', 'P', 'N', 'G', '\r', '\n', '\x1a', '\n', '\x00',
               '\x00', '\x00', '\r', 'I', 'H', 'D', 'R', '\x00',
               '\x00', '\x00', '\x01', '\x00', '\x00', '\x00', '\x01',
               '\x08', '\x02', '\x00', '\x00', '\x00', '\x90',
               'w', 'S', '\xde', '\x00', '\x00', '\x00', '\x06', 'b', 'K',
               'G', 'D', '\x00', '\x00', '\x00', '\x00',
               '\x00', '\x00', '\xf9', 'C', '\xbb', '\x7f', '\x00', '\x00',
               '\x00', '\t', 'p', 'H', 'Y', 's', '\x00',
               '\x00', '\x0e', '\xc3', '\x00', '\x00', '\x0e', '\xc3',
               '\x01', '\xc7', 'o', '\xa8', 'd', '\x00', '\x00',
               '\x00', '\x07', 't', 'I', 'M', 'E', '\x07', '\xe0', '\x05',
               '\r', '\x08', '%', '/', '\xad', '+', 'Z',
               '\x89', '\x00', '\x00', '\x00', '\x0c', 'I', 'D', 'A', 'T',
               '\x08', '\xd7', 'c', '\xf8', '\xff', '\xff',
               '?', '\x00', '\x05', '\xfe', '\x02', '\xfe', '\xdc', '\xcc',
               'Y', '\xe7', '\x00', '\x00', '\x00', '\x00',
               'I', 'E', 'N', 'D', '\xae', 'B', '`', '\x82']
    
    valid_png_bin = str.encode("".join(png_hex))
    png = SimpleUploadedFile("test.png", valid_png_bin)
    
    post_dict = {'title': 'Test Title'}
    file_dict = {'picture': png}
    
    form = MyForm(data=post_dict, files=file_dict)
    

    【讨论】:

      【解决方案3】:

      这是另一种不需要您使用实际图像的方式。

      编辑:针对 Python 3 更新。

      from PIL import Image
      from io import BytesIO # Python 2: from StringIO import StringIO
      from django.core.files.uploadedfile import InMemoryUploadedFile
      
      ...
      
      def test_form(self):
          im = Image.new(mode='RGB', size=(200, 200)) # create a new image using PIL
          im_io = BytesIO() # a BytesIO object for saving image
          im.save(im_io, 'JPEG') # save the image to im_io
          im_io.seek(0) # seek to the beginning
      
          image = InMemoryUploadedFile(
              im_io, None, 'random-name.jpg', 'image/jpeg', len(im_io.getvalue()), None
          )
      
          post_dict = {'title': 'Test Title'}
          file_dict = {'picture': image}
      
          form = MyForm(data=post_dict, files=file_dict)
      

      【讨论】:

      • 当前存在下一个但是:TypeError: string argument expected, got 'bytes'。我使用BytesIO 而不是StringIO 解决了它,没有使用im_io.len,因为BytesIO 对象没有长度属性。
      • @SalahAdDin 感谢您的提醒。我在 Python 2.7 中测试了上面的代码,它工作正常。您能告诉我您使用的是哪个版本的 Python 和 Django 吗?我想为未来的读者更正上面的代码。谢谢。
      • 我正在使用django 1.9 和python 3.5。
      【解决方案4】:

      到目前为止,我发现这种方法可行

      from django.core.files.uploadedfile import SimpleUploadedFile
       ...
      def test_form(self):
              upload_file = open('path/to/file', 'rb')
              post_dict = {'title': 'Test Title'}
              file_dict = {'file': SimpleUploadedFile(upload_file.name, upload_file.read())}
              form = MyForm(post_dict, file_dict)
              self.assertTrue(form.is_valid())
      

      【讨论】:

      • 根据您上传代码的作用,您可能还需要设置upload_file 的content_type 属性
      • 其中 content_type 是一个字符串,例如 'image/jpeg' 用于 jpg 图像文件。
      • 我收到了TypeError: 'str' does not support the buffer interface。
      • 我想说这应该使用with 构造来完成,以避免必须显式关闭upload_file。
      • 添加files=form_data 为我解决了这个问题。不明显,但为 python3 更新了部分答案:stackoverflow.com/a/34276961/4028977
      【解决方案5】:

      可能这不太正确,但我正在使用 StringIO 在单元测试中创建图像文件:

      imgfile = StringIO('GIF87a\x01\x00\x01\x00\x80\x01\x00\x00\x00\x00ccc,\x00'
                           '\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02D\x01\x00;')
      imgfile.name = 'test_img_file.gif'
      
      response = self.client.post(url, {'file': imgfile})
      

      【讨论】:

      • 我尝试与视图分开测试表单。其实我还没拍完。
      • 好吧,看来 SimpleUploadedFile 是一个非常清晰的测试解决方案
      猜你喜欢
      • 2017-01-21
      • 2013-06-14
      • 2011-05-16
      • 2020-11-13
      • 2020-04-23
      • 1970-01-01
      • 2018-09-21
      • 1970-01-01
      • 2023-04-09
      相关资源
      最近更新 更多