Django 模拟自定义文件存储后端
我已经创建了一个自定义文件存储后端,它使用boto调用AmazonS3并将文件存储在那里(我知道django storages也会处理这个问题,但我们遇到了一些问题)。我将其存储在utils模块中,并在我的模型中使用,如下所示:Django 模拟自定义文件存储后端,django,unit-testing,amazon-s3,Django,Unit Testing,Amazon S3,我已经创建了一个自定义文件存储后端,它使用boto调用AmazonS3并将文件存储在那里(我知道django storages也会处理这个问题,但我们遇到了一些问题)。我将其存储在utils模块中,并在我的模型中使用,如下所示: from utils.s3 import S3Storage class Photo(models.Model): image = models.ImageField(storage=S3Storage(), upload_to="images") 因此,每当使
from utils.s3 import S3Storage
class Photo(models.Model):
image = models.ImageField(storage=S3Storage(), upload_to="images")
因此,每当使用图像文件创建照片时,图像文件都会上载到S3存储桶
我不想在测试期间调用S3,但很难弄清楚在这种情况下要模拟什么。我无法模拟整个图像字段,因为我需要通过Tastypie测试创建模型
有什么想法吗 您可以在
S3Storage
类中模拟\u save
方法,以避免上传到S3。您可以改用FileSystemStorage
对于您的案例,我的解决方案如下:
import mock
from utils.s3 import S3Storage
from django.core.files.storage import FileSystemStorage
fss = FileSystemStorage()
@mock.patch.object(S3Storage, '_save', fss._save)
def test_something():
assert True
我尝试了许多其他解决方案,比如在设置
默认文件存储中覆盖或Manh-Tai的解决方案。所有问题都是Django在初始化时在内存中加载所有模型,这使得在设置模型属性后修改模型属性有点不直观
使用Django 2.1和Python 3进行测试:
来自unittest.mock导入MagicMock
从django.core.files.storage导入存储
从django.core.files.uploadedfile导入SimpleUploadedFile
类CreatePhotoTest(测试用例):
def测试后照片(自身):
def生成_文件名(文件名):
返回文件名
def保存(名称、内容、最大长度):
返回名称
存储\u mock=MagicMock(spec=storage,name='StorageMock')
存储\u mock.generate\u filename=生成\u filename
存储\u mock.save=MagicMock(副作用=保存)
storage_mock.url=MagicMock(name='url')
存储\u mock.url.return\u value='0http://example.com/generated_filename.png'
照片。_meta.get_字段('image')。存储=存储
img=SimpleUploadedFile('file.png',b“file\u content”,content\u type=“image/png”)
数据={
“已签署的合同”:img
}
response=self.client.post('/endpoint',data,format='multipart')
self.assertTrue(存储\u mock.save.called)
生成的\u filename=storage\u mock.save.call\u args\u list[0][0]
上传的文件=存储模拟保存调用参数列表[0][0][1]
self.assertEqual(上传的_file.name,'file.pdf')
我创建了generate\u filename()
和save()
,但如果您不想这样做,就不需要这样做。这样做只是为了尽可能多地模拟真实存储器的行为,并在测试中对其进行验证。类似的操作可以用于pytest:
import pytest
import os
from django.core.files.storage import get_storage_class
@pytest.fixture
def mock_storage(monkeypatch):
"""
Mocks the backend storage system by not actually accessing media
"""
clean_name = lambda name: os.path.splitext(os.path.basename(name))[0]
def _mock_save(instance, name, content):
setattr(instance, f"mock_{clean_name(name)}_exists", True)
return str(name).replace('\\', '/')
def _mock_delete(instance, name):
setattr(instance, f"mock_{clean_name(name)}_exists", False)
pass
def _mock_exists(instance, name):
return getattr(instance, f"mock_{clean_name(name)}_exists", False)
storage_class = get_storage_class()
monkeypatch.setattr(storage_class, "_save", _mock_save)
monkeypatch.setattr(storage_class, "delete", _mock_delete)
monkeypatch.setattr(storage_class, "exists", _mock_exists)
然后,这是否会写入文件系统(在测试期间您不希望执行的其他操作)?我想您可能不希望在save()
上发生任何事情,然后将其写入文件系统,但我认为这是可以接受的,因为它不是第三方API,我们最终会在运行测试后破坏测试环境。您也可以使用带有返回值的模拟,而不是使用真正的\u save
方法:@patch.object(GoogleCloudMediaStorage,'.'u save',MagicMock(返回值='/tmp/plain.pdf'))
@mock.patch.object(S3boto3存储,'.'u save',lambda self,name,content:name)
完成这项工作。