【问题标题】:What is the clean way to unittest FileField in django?在 django 中对 FileField 进行单元测试的干净方法是什么?
【发布时间】:2011-05-16 02:21:06
【问题描述】:

我有一个带有 FileField 的模型。我想对它进行单元测试。 django 测试框架有很好的方法来管理数据库和电子邮件。 FileFields 有类似的东西吗?

如何确保单元测试不会污染实际应用程序?

提前致谢

PS:我的问题几乎与Django test FileField using test fixtures 重复,但它没有公认的答案。只是想再次询问有关此主题的新内容。

【问题讨论】:

标签: python django filefield django-unittest


【解决方案1】:

我想最简单的方法是使用 ContentFile 类:

file = ContentFile('text', 'name')
my_model = MyModel()
my_model.file = file
my_model.save()

【讨论】:

    【解决方案2】:

    Django 提供了一个很好的方法来做到这一点 - 使用 SimpleUploadedFileTemporaryUploadedFile。如果您只需要存储一些标记数据,SimpleUploadedFile 通常是更简单的选项:

    from django.core.files.uploadedfile import SimpleUploadedFile
    
    my_model.file_field = SimpleUploadedFile(
        "best_file_eva.txt",
        b"these are the file contents!"   # note the b in front of the string [bytes]
    )
    

    这是 django 的神奇功能之一——在文档中不显示 :)。但是它被引用here 并实现here

    限制

    请注意,您只能将bytes 放入SimpleUploadedFile,因为它是在幕后使用BytesIO 实现的。如果您需要更真实的、类似文件的行为,您可以使用TemporaryUploadedFile

    对于 Python 2

    如果你被python 2卡住了,跳过内容中的b前缀:

    my_model.file_field = SimpleUploadedFile(
        "best_file_eva.txt",
        "these are the file contents!" # no b
    )
    

    【讨论】:

    • 我不知道这个。谢谢你的评论。我会调查的
    • 内容必须在 Python 2.x 中为 str(),在 Python 3.x 中为 bytes()。你不能在里面放文本/unicode。
    • 我知道我会在 4 年后问这个问题,但是......有没有办法伪造文件大小?
    • 这对我有用,但文件被保存在我不想要的磁盘上。我通过模拟本文中所述的文件存储后端解决了这个问题cscheng.info/2018/08/21/…
    【解决方案3】:

    如果您只想创建一个需要 FileField 的对象并且 不想使用此字段,那么您可以传递任何(现有或不存在的)相对路径像这样:

    example_object = models.ExampleModel({'file': "foo.bar"})
    example_object.save()
    

    然后就可以使用了。

    【讨论】:

      【解决方案4】:

      有几种方法可以解决这个问题,但它们都很丑,因为单元测试应该是隔离的,但文件都是关于持久更改的。

      我的单元测试不能在具有生产数据的系统上运行,因此在每次运行后使用git reset --hard 之类的内容重置上传目录很容易。这种方法在某些方面是最好的,因为它不涉及代码更改,并且只要您从良好的测试数据开始就可以保证工作。

      如果您在测试模型的保存方法后实际上不需要对该文件执行任何操作,我建议您使用 python 出色的 Mock library 来完全伪造 File 实例(即类似于 mock_file = Mock(spec=django.core.files.File); mock_file.read.return_value = "fake file contents")所以您可以完全避免更改文件处理逻辑。 Mock 库有几种方法可以在测试方法中使用globally patch Django 的File class,这将变得非常简单。

      如果您需要一个真实文件(即作为测试的一部分,使用外部脚本进行处理等),您可以使用类似于 Mirko 示例的内容并在确保它会创建一个File object存储在适当的地方 - 这里有三种方法可以做到这一点:

      • 让您的测试settings.MEDIA_ROOT 指向一个临时目录(参见Python tempfile 模块的mkdtemp 函数)。只要您有一个单独的 STATIC_ROOT 之类的东西,它就可以很好地用于源代码中的媒体文件。
      • 使用自定义storage manager
      • 在每个 File 实例上手动设置文件路径,或使用自定义 upload_to 函数指向您的测试设置/拆卸过程清除的某个位置,例如 MEDIA_ROOT 下的测试子目录。

      编辑:模拟对象库是python 3.3 版中的新功能。对于较旧的 python 版本,请检查Michael Foord's version

      【讨论】:

      • 感谢您的出色回答。我最终选择通过在我的测试用例的 setUp 中更改此变量的值来指向单元测试 MEDIA_ROOT
      • mock 现在是 Python 标准库的一部分,在 Python 3.3 及更高版本中以 unittest.mock 的形式提供 - 从 REAME.rst 收集
      • @chachan 你能建议编辑吗?这个答案肯定需要更新
      【解决方案5】:

      我通常使用 doctest 测试模型中的文件字段

      >>> from django.core.files import File
      >>> s = SimpleModel()
      >>> s.audio_file = File(open("media/testfiles/testaudio.wav"))
      >>> s.save()
      >>> ...
      >>> s.delete()
      

      如果需要,我还可以使用测试客户端测试文件上传。

      至于夹具,我只是在修改夹具中的路径后,将我需要的文件复制到测试文件夹中。

      例如

      在包含模型的夹具中,文件文件指向名为“audio”的目录,您将“audio”:“audio/audio.wav”替换为“audio”:“audio/test/audio.wav”。
      现在您所要做的就是在测试设置的“音频”中复制包含必要文件的测试文件夹,然后在拆解中将其删除。

      不是我认为最干净的方式,但我就是这样做的。

      【讨论】:

      • 是否需要手动关闭文件句柄才能释放它使用的资源?
      猜你喜欢
      • 2011-01-29
      • 2011-12-02
      • 2022-10-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-06
      • 2017-02-20
      相关资源
      最近更新 更多