如何在单元测试中优雅地交换(修补)Django FileSystemStorage 设置?

发布于 2024-10-15 08:01:31 字数 1096 浏览 5 评论 0原文

我面临以下问题。我有一个看起来有点像这样的模型:

class Package(models.Model):
    name = models.CharField(max_length=64)
    file = models.FileField(upload_to="subdir",
                            storage=settings.PACKAGE_STORAGE,
                            null=True)

此示例中的基本内容是 FileField 构造函数的 storage= 参数。它填充了来自 settings.py 的值。其中有以下代码:

from django.core.files.storage import FileSystemStorage
PACKAGE_STORAGE = FileSystemStorage(location="/var/data", base_url="/")

对于生产使用,效果很好。但在我的单元测试中,我上传的内容现在写入 /var/data,其中包含生产数据。我尝试像这样交换packages/tests.py中的PACKAGE_STORE

from django.conf import settings     # This is line 1
from tempfile import mkdtemp
settings.PACKAGE_STORAGE = FileSystemStorage(location=mkdtemp(), base_url="/")

# rest of the imports and testing code below

但真正的问题是加载测试文件之前, packages 应用程序及其模型已加载,因此,在我能够在测试设置代码中更改它之前,PACKAGE_STORAGE 设置已得到解决。

是否有一种优雅的方法可以在测试上下文中覆盖此特定设置?

I'm faced with the following problem. I have a Model that looks somewhat like this:

class Package(models.Model):
    name = models.CharField(max_length=64)
    file = models.FileField(upload_to="subdir",
                            storage=settings.PACKAGE_STORAGE,
                            null=True)

Essential in this example is the storage= argument to the FileField constructor. It is filled with a value from settings.py. In there is the following code:

from django.core.files.storage import FileSystemStorage
PACKAGE_STORAGE = FileSystemStorage(location="/var/data", base_url="/")

For production use, this works fine. But in my unit tests, uploads I make are now written to /var/data, which contains production data. I tried to swap out the PACKAGE_STORE in packages/tests.py like this

from django.conf import settings     # This is line 1
from tempfile import mkdtemp
settings.PACKAGE_STORAGE = FileSystemStorage(location=mkdtemp(), base_url="/")

# rest of the imports and testing code below

but the real problem is that before the test file is loaded, the packages app and its models have been loaded already, and therefore, the PACKAGE_STORAGE setting has been resolved before I'm able to change it in the test setup code.

Is there an elegant way to override this specific setting in a testing context?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

不醒的梦 2024-10-22 08:01:31

不知道这是否算优雅,但您可以使用不同的设置文件进行测试...

类似:

# test_settings.py

from settings import *

PACKAGE_STORAGE = FileSystemStorage(location='/test/files', base_url="/")

然后使用测试设置运行测试,python manage.py test --settings=test_settings.

Don't know if this counts as elegant, but you could use a different settings file for testing...

Something like:

# test_settings.py

from settings import *

PACKAGE_STORAGE = FileSystemStorage(location='/test/files', base_url="/")

Then run your test using the test settings, python manage.py test --settings=test_settings.

っ左 2024-10-22 08:01:31

动态覆盖模型上 FileField 实例的底层存储实现:

def setUp(self):
     self._field = Package._meta.get_field_by_name('file')[0]
     self._default_storage = self._field.storage
     test_storage = FileSystemStorage(location=mkdtemp(),
                                      base_url="/")

     self._field.storage = test_storage

def tearDown(self):
     self._field = self._default_storage

Override the underlying storage implementation for instances of FileField on your model dynamically:

def setUp(self):
     self._field = Package._meta.get_field_by_name('file')[0]
     self._default_storage = self._field.storage
     test_storage = FileSystemStorage(location=mkdtemp(),
                                      base_url="/")

     self._field.storage = test_storage

def tearDown(self):
     self._field = self._default_storage
会傲 2024-10-22 08:01:31

如果你通过 django 运行测试,这

if 'test' in sys.argv:
    settings.DEFAULT_FILE_STORAGE = FileSystemStorage(location=mkdtemp(), base_url="/")

当然应该可以工作;)

DEFAULT_FILE_STORAGE = FileSystemStorage(location="/var/data", base_url="/")

if you run the test through django this should work

if 'test' in sys.argv:
    settings.DEFAULT_FILE_STORAGE = FileSystemStorage(location=mkdtemp(), base_url="/")

of course after ;)

DEFAULT_FILE_STORAGE = FileSystemStorage(location="/var/data", base_url="/")
青柠芒果 2024-10-22 08:01:31

我刚刚通过添加到我的自定义测试运行程序来解决这个问题。要了解如何添加自定义测试运行程序,请参阅 在 Django 文档中定义测试运行器。我的代码看起来像这样:

import shutil
import tempfile
from django.test.simple import DjangoTestSuiteRunner
from django.conf import settings

class CustomTestRunner(DjangoTestSuiteRunner):

    def setup_test_environment(self, **kwargs):
        super(CustomTestRunner, self).setup_test_environment(**kwargs)
        self.backup = {}
        self.backup['DEFAULT_FILE_STORAGE'] = settings.DEFAULT_FILE_STORAGE
        settings.DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage'
        self.backup['MEDIA_ROOT'] = settings.MEDIA_ROOT
        self.temp_media_root = tempfile.mkdtemp(prefix="myapp-tests")
        settings.MEDIA_ROOT = self.temp_media_root

    def teardown_test_environment(self, **kwargs):
        super(CustomTestRunner, self).teardown_test_environment(**kwargs)
        for name, value in self.backup.iteritems():
            setattr(settings, name, value)

    def run_tests(self, test_labels, **kwargs):
        try:
            test_results = super(CustomTestRunner, self).run_tests(test_labels, **kwargs)
        finally:
            shutil.rmtree(self.temp_media_root, ignore_errors=True)

这会覆盖一些自定义测试套件方法。 setup_test_environment 备份以前的设置并将其存储在类属性中。 teardown_test_environment 将它们设置回之前的状态。 run_tests 方法使用 try/finally 块来确保测试后删除临时目录,即使发生异常也是如此。

I just solved this by adding to my custom test runner. To see how to add a custom test runner, see Defining a test runner in the Django documentation. My code looks something like this:

import shutil
import tempfile
from django.test.simple import DjangoTestSuiteRunner
from django.conf import settings

class CustomTestRunner(DjangoTestSuiteRunner):

    def setup_test_environment(self, **kwargs):
        super(CustomTestRunner, self).setup_test_environment(**kwargs)
        self.backup = {}
        self.backup['DEFAULT_FILE_STORAGE'] = settings.DEFAULT_FILE_STORAGE
        settings.DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage'
        self.backup['MEDIA_ROOT'] = settings.MEDIA_ROOT
        self.temp_media_root = tempfile.mkdtemp(prefix="myapp-tests")
        settings.MEDIA_ROOT = self.temp_media_root

    def teardown_test_environment(self, **kwargs):
        super(CustomTestRunner, self).teardown_test_environment(**kwargs)
        for name, value in self.backup.iteritems():
            setattr(settings, name, value)

    def run_tests(self, test_labels, **kwargs):
        try:
            test_results = super(CustomTestRunner, self).run_tests(test_labels, **kwargs)
        finally:
            shutil.rmtree(self.temp_media_root, ignore_errors=True)

This overrides some of the custom test suite methods. setup_test_environment backs up the previous settings and stores them in a class attribute. The teardown_test_environment sets them back to what they were before. The run_tests method uses a try/finally block to make sure that the temporary directory is deleted after the tests, even if an exception happens.

抚你发端 2024-10-22 08:01:31

如果您使用 pytest-django 并希望保留单个 Django 配置文件,您可以创建一个conftest.py 并为每个执行的测试覆盖该设置()

@pytest.fixture(autouse=True)
def use_file_system_storage(settings):
    settings.PACKAGE_STORAGE = FileSystemStorage(location=mkdtemp(), base_url="/")

您还可以在 Django 设置之前更改您的应用程序使用pytest-django。请参阅此处

If you are using pytest-django and want to keep a single Django configuration file you can create a conftest.py and have that setting overridden for every test executed (source)

@pytest.fixture(autouse=True)
def use_file_system_storage(settings):
    settings.PACKAGE_STORAGE = FileSystemStorage(location=mkdtemp(), base_url="/")

You can also change your app before Django sets up using pytest-django. See here.

聊慰 2024-10-22 08:01:31

回复晚了,但我花了很长时间才找到这个漂亮的小图书馆;

https://pypi.org/project/django-override-storage/

Late answer but it took me a long time to find this nice little library;

https://pypi.org/project/django-override-storage/

梦里南柯 2024-10-22 08:01:31

对于产品,我们使用 s3 和 boto3,但对于单元测试,使用很棒的 lib dj-inmemorystorage 只是将文件加载到内存中。
因此,我只是覆盖测试设置中的默认文件存储:

DEFAULT_FILE_STORAGE = 'inmemorystorage.InMemoryStorage'

然后在测试中:

record.receipt_file = SimpleUploadedFile('receipt.txt', b'')

For prod we use a s3 and boto3 but for unit tests it is enough to use great lib dj-inmemorystorage which just loads the file to memory.
So, I just override the default file storage in my test settings:

DEFAULT_FILE_STORAGE = 'inmemorystorage.InMemoryStorage'

then in the test:

record.receipt_file = SimpleUploadedFile('receipt.txt', b'')
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文