模拟自定义文件存储后端

时间:2014-03-28 21:00:19

标签: django unit-testing amazon-s3

我已经创建了一个自定义文件存储后端,使用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测试创建模型。

有什么想法吗?

3 个答案:

答案 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)