我有一个带有 ImageField 的模型,由 django-storages 的 S3Boto 支持。 我对“上传图像”视图进行了测试,但事实上,它将图像上传到 S3 减慢了我的测试套件的速度。
为了加快测试速度,处理此问题的最佳实践是什么? 我应该模拟 S3Boto 吗? 也许有一个内存支持的存储后端非常适合测试(自动清理会很好!)?
dj-inmemorystorage
,我获得了更快的测试速度。
设置此功能的快速方法是在与您的设置相同的文件夹中创建一个
test_settings.py
:
from settings import *
DEFAULT_FILE_STORAGE = 'inmemorystorage.InMemoryStorage'
...并调用
./manage.py test --settings=project.test_settings
来运行测试。
我的首选方法是设置自定义测试运行程序:
在
project/test_runner.py
:
from django.conf import settings
from django.test.runner import DiscoverRunner
class FastTestRunner(DiscoverRunner):
def setup_test_environment(self):
super(FastTestRunner, self).setup_test_environment()
# Don't write files
settings.DEFAULT_FILE_STORAGE = 'inmemorystorage.InMemoryStorage'
# Bonus: Use a faster password hasher. This REALLY helps.
settings.PASSWORD_HASHERS = (
'django.contrib.auth.hashers.MD5PasswordHasher',
)
注意: 这也会设置
PASSWORD_HASHER
,因为它显着改善了User
创建时间。 这不应该在生产中设置。
在
project/settings.py
:
TEST_RUNNER = 'project.test_runner.FastTestRunner'
要求:
pip install dj-inmemorystorage
django-inmemorystorage
更改为 dj-inmemorystorage
。
django-discover-runner
,因为它现在是 django 中的默认测试运行器,并修复了 PASSWORD_HASHER
相关博客文章的链接。
我也遇到了这个问题,所以我想我应该把我的解决方案提出来。 我的解决方案使用
Mock
import mock
from django.core.files.storage import FileSystemStorage
from django.test import TestCase
class ATestCase(TestCase):
def setUp(self):
# Stuff Happens
def tearDown(self):
# more Stuff
@mock.patch('storages.backends.s3boto.S3BotoStorage', FileSystemStorage)
def test_file_stuff(self):
self.assertMagicPonies(True)
一些问题 - 确保您在设置中进行了合理的
MEDIA_ROOT
MEDIA_ROOT
,因此您需要为其单独的设置配置 (https://code.djangoproject.com/ticket/17787) 我也使用 S3Boto,但为了测试,我更喜欢自定义设置,其中包括使用文件系统存储。您可以在文件中声明自定义设置,然后可以在测试用例中导入并使用该设置。即使如此,您也可以模拟文件存储,以便文件实际上不会写入磁盘。
这是一个示例
test_settings.py
# myproject/myproject/test_settings.py
from django.test import override_settings
common_settings = override_settings(
DEFAULT_FILE_STORAGE='django.core.files.storage.FileSystemStorage',
PASSWORD_HASHERS=(
'django.contrib.auth.hashers.UnsaltedMD5PasswordHasher',
),
)
用途:
from django.test import TestCase
from myproject.test_settings import common_settings
@common_settings
class MyTestCase(TestCase):
"""Tests go here"""
在模拟文件系统存储时,您可以查看我的答案here on SO。
我知道这是一个相当古老的线程,但 Django 4.2 现在通过
InMemoryStorage
提供了一种更简单的方法。您只需覆盖设置来声明它,所有媒体文件访问都将在内存中模拟:
@override_settings(STORAGES={
"default": {
"BACKEND": "django.core.files.storage.memory.InMemoryStorage",
},
})
class StorageTest(TestCase):
"""Any upload test will only use memory here"""
...
我建议使用标准 Django 存储进行测试,您可以定义自定义存储路径,并在完成后清理测试套件中的该路径。存储和路径都可以在设置中设置并覆盖以进行测试。
在我的例子中,需要使用近乎真实的 S3 后端,因为我与 S3 有其他基于
boto3
的交互。 (Django 5.1,这将来可能会中断)
moto
提供了一套很好的AWS模拟,包括S3。然而,仅仅用 mock_aws
装饰器或上下文管理器包装所有内容是不够的,因为存储是延迟初始化和全局缓存的。
在深入研究 Django 和
django-storages
内部一段时间后,我最终得到了以下 pytest
固定装置(monkeypatch
是 pytest 的内置装置,settings
固定装置来自 pytest-django
):
@pytest.fixture
def _mock_aws(monkeypatch, settings):
import boto3
def reinit_storages():
# See https://github.com/getmoto/moto/issues/3446
# Patch all boto3 uses including django-storages
from django.core.files import storage
storage.storages._storages = {}
storage.default_storage._wrapped = storage.storages["default"]
with monkeypatch.context() as m:
m.setenv("AWS_ACCESS_KEY_ID", "testing")
m.setenv("AWS_SECRET_ACCESS_KEY", "testing")
m.setenv("AWS_SECURITY_TOKEN", "testing")
m.setenv("AWS_SESSION_TOKEN", "testing")
m.setenv("AWS_DEFAULT_REGION", "us-east-1")
with mock_aws():
boto3.client("s3").create_bucket(Bucket=settings.AWS_STORAGE_BUCKET_NAME)
reinit_storages()
yield
reinit_storages()
关键部件:
django.core.files.storage.storages._storages
default_storage
的内部_wrapped
(您不能直接分配给default_storage
,因为模型元中的FileField
实例引用了它)mock_aws
moto
模拟完全空的基础设施,但会记住您的操作)此设置似乎模拟和取消模拟所有内容,使用真实 S3 存储桶的集成测试在同一会话中按预期工作,无论是在使用
_mock_aws
进行测试之前还是之后。