我已经创建了一个自定义文件存储后端,使用boto调用Amazon S3并将文件存储在那里(我知道django-storages也处理这个问题,但我们遇到了几个问题)。我将它存储在一个utils模块中并在我的模型中使用它,如下所示:
from utils.s3 import S3Storage
class Photo(models.Model):
image = models.ImageField(storage=S3Storage(), upload_to="images")
因此,只要用图像文件创建照片,图像文件就会上传到S3存储桶。
我不想在测试期间给S3打电话,但很难搞清楚在这种情况下要嘲笑什么。我无法模拟整个图像领域,因为我需要通过Tastypie测试创建模型。
有什么想法吗?
答案 0 :(得分:2)
您可以在_save
类中模拟S3Storage
方法,以避免上传到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
答案 1 :(得分:0)
我尝试了许多其他解决方案,例如覆盖设置DEFAULT_FILE_STORAGE
或Manh Tai的解决方案。一切问题都在于,Django在初始化时会将所有模型加载到内存中,一旦设置了模型属性,修改模型属性就变得不太直观。
经过Django 2.1和Python 3的测试:
from unittest.mock import MagicMock
from django.core.files.storage import Storage
from django.core.files.uploadedfile import SimpleUploadedFile
class CreatePhotoTest(TestCase):
def test_post_photo(self):
def generate_filename(filename):
return filename
def save(name, content, max_length):
return name
storage_mock = MagicMock(spec=Storage, name='StorageMock')
storage_mock.generate_filename = generate_filename
storage_mock.save = MagicMock(side_effect=save)
storage_mock.url = MagicMock(name='url')
storage_mock.url.return_value = 'http://example.com/generated_filename.png'
Photo._meta.get_field('image').storage = storage_mock
img = SimpleUploadedFile('file.png', b"file_content", content_type="image/png")
data = {
'signed_contract': img
}
response = self.client.post('/endpoint', data, format='multipart')
self.assertTrue(storage_mock.save.called)
generated_filename = storage_mock.save.call_args_list[0][0][0]
uploaded_file = storage_mock.save.call_args_list[0][0][1]
self.assertEqual(uploaded_file.name, 'file.pdf')
我创建了generate_filename()
和save()
,但是如果您不想这样做,则不需要这样做。这样做是为了尽可能模拟真实存储的行为并在测试中进行验证。
答案 2 :(得分:0)
像这样的东西可能对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)