我有一个带 FileField 的模型。我想把它统一起来。Django 测试框架有很好的方法来管理数据库和电子邮件。FileFields 有类似的东西吗?
如何确保单元测试不会污染实际的应用程序?
先谢谢你
PS: 我的问题几乎是 Django 使用测试装置测试 FileField的副本,但它没有一个被接受的答案。只是想再问一下这个话题有没有什么新东西。
我通常使用 doctest 在模型中测试文件字段
>>> from django.core.files import File >>> s = SimpleModel() >>> s.audio_file = File(open("media/testfiles/testaudio.wav")) >>> s.save() >>> ... >>> s.delete()
如果需要,我还可以用测试客户端测试文件上传。
至于 fixture,我只是在修改 fixture 中的路径之后,将需要的文件复制到测试文件夹中。
例如:。
在一个包含模型的 fixture 中,文件字段指向一个名为“ Audio”的目录,您可以将“ Audio”: “ Audio/audo.wav”替换为“ Audio”: “ Audio/test/audo.wav”。 现在您所要做的就是将测试文件夹和必要的文件复制到 test setUp 中的“ Audio”中,然后在 tearDown 中删除它。
不是我想过的最干净的方式,但我就是干这个的。
有几种方法可以解决这个问题,但是它们都很难看,因为单元测试应该是独立的,但是文件都是关于持久更改的。
我的单元测试不运行在带有生产数据的系统上,所以很容易在每次运行后重置上传目录,比如使用 git reset --hard。从某些方面来说,这种方法是最好的,因为它不涉及代码更改,并且只要您从良好的测试数据开始,它就一定能够工作。
git reset --hard
如果在测试了模型的保存方法之后,您实际上不需要对该文件做任何事情,我建议使用 python 优秀的 模拟图书馆来完全伪造 File实例(比如类似于 mock_file = Mock(spec=django.core.files.File); mock_file.read.return_value = "fake file contents"的东西) ,这样您就可以完全避免对文件处理逻辑的更改。Mock 库有几种方法可以在测试方法中实现 全球补丁 Django 的 文件类,这种方法非常简单。
File
mock_file = Mock(spec=django.core.files.File); mock_file.read.return_value = "fake file contents"
如果你需要一个真正的文件(例如,作为测试的一部分,使用外部脚本处理等) ,你可以使用类似于 Mirko 的例子,并在确保它存储在合适的地方之后创建一个 文件对象——这里有三种方法:
settings.MEDIA_ROOT
mkdtemp
STATIC_ROOT
MEDIA_ROOT
编辑: 模拟对象库在 python 版本3.3中是新的。对于旧的 python 版本,请检查 < a href = “ http://www.voidspace.org.uk/python/mock/”rel = “ noReferrer”> Michael Foord 的版本
如果你只想要 创建一个对象需要 FileField 和 不想使用这个领域,那么你可以像这样传递任何(现有的或不存在的)相对路径:
example_object = models.ExampleModel({'file': "foo.bar"}) example_object.save()
然后就可以开始使用了。
Django 为此提供了一个很好的方法——使用 SimpleUploadedFile或 TemporaryUploadedFile。如果您需要存储的只是一些前哨数据,那么 SimpleUploadedFile通常是更简单的选择:
SimpleUploadedFile
TemporaryUploadedFile
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 的神奇特性之一——不会出现在文档中:) ,但是它引用了 给你并实现了 给你。
注意,只能将 bytes放在 SimpleUploadedFile中,因为它是在幕后使用 BytesIO实现的。如果您需要更现实的、类似文件的行为,可以使用 TemporaryUploadedFile。
bytes
BytesIO
如果 你卡在 python 2上了,跳过内容中的 b前缀:
b
my_model.file_field = SimpleUploadedFile( "best_file_eva.txt", "these are the file contents!" # no b )
我想最简单的方法是使用 ContentFile 类:
file = ContentFile('text', 'name') my_model = MyModel() my_model.file = file my_model.save()
有一些方法可以模拟仓库。
在读取 django 源代码之后,下面是我的实现,以避免将文件上传到 s3,无论是使用默认存储还是将存储分配到 filefield 或 image 字段。
from unittest.mock import patch from django.db.models.fields.files import FileField, FieldFile, ImageField, ImageFieldFile from django.core.files.storage import FileSystemStorage from rest_framework.test import APITestCase class CustomFileSystemStorage(FileSystemStorage): def url(self, *args, **kwargs): return self.path(*args, **kwargs) class CustomFieldFile(FieldFile): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.storage = CustomFileSystemStorage() class CustomImageFieldFile(ImageFieldFile): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.storage = CustomFileSystemStorage() class PatchMeta(type): """A metaclass to patch all inherited classes.""" def __new__(meta, name, bases, attrs): cls = type.__new__(meta, name, bases, attrs) cls = patch.object(ImageField, 'attr_class', CustomImageFieldFile)(cls) cls = patch.object(FileField, 'attr_class', CustomFieldFile)(cls) return cls class CustomBaseTestCase(APITestCase, metaclass=PatchMeta): """You can inherit this class to do any testcase for mocking storage""" pass
通过继承 CustomBaseTestCase类,可以更改存储以避免将文件上传到任何服务器