Django 使用测试装置测试 FileField

发布于 2024-08-22 00:03:00 字数 1226 浏览 8 评论 0原文

我正在尝试为某些具有 FileField 的模型构建测试。模型如下:

class SolutionFile(models.Model):
    '''
    A file from a solution.
    '''
    solution = models.ForeignKey(Solution)
    file = models.FileField(upload_to=make_solution_file_path)

我遇到了两个问题:

  1. 使用./manage.py dumpdata将数据保存到fixture时,文件内容没有保存,只保存了文件名固定装置。虽然我发现这是预期的行为,因为文件内容未保存到数据库中,但我想以某种方式在测试夹具中包含此信息。

  2. 我有一个用于上传文件的测试用例,如下所示:

    def test_post_solution_file(自身):
        导入临时文件
        导入操作系统
        文件名 = tempfile.mkstemp()[1]
        f = 打开(文件名,'w')
        f.write('这些是文件内容')
        f.close()
        f = 打开(文件名,'r')
        post_data = {'文件': f}
        响应 = self.client.post(self.solution.get_absolute_url()+'add_solution_file/', post_data,
                                    遵循=真)
        f.close()
        os.remove(文件名)
        self.assertTemplateUsed(响应,'tests/solution_detail.html')
        self.assertContains(响应, os.path.basename(文件名))
    

虽然此测试工作得很好,但完成后它会将上传的文件保留在媒体目录中。当然,删除可以在tearDown()中处理,但我想知道Django是否有另一种方法来处理这个问题。

我想到的一种解决方案是使用不同的媒体文件夹进行测试,该文件夹必须与测试装置保持同步。运行测试时,有什么方法可以在 settings.py 中指定另一个媒体目录?我可以包含某种转储数据的挂钩,以便它同步媒体文件夹中的文件吗?

那么,是否有一种更 Pythonic 或 Django 特定的方法来处理涉及文件的单元测试?

I'm trying to build tests for some models that have a FileField. The model looks like this:

class SolutionFile(models.Model):
    '''
    A file from a solution.
    '''
    solution = models.ForeignKey(Solution)
    file = models.FileField(upload_to=make_solution_file_path)

I have encountered two problems:

  1. When saving data to a fixture using ./manage.py dumpdata, the file contents are not saved, only the file name is saved into the fixture. While I find this to be the expected behavior as the file contents are not saved into the database, I'd like to somehow include this information in the fixture for tests.

  2. I have a test case for uploading a file that looks like this:

    def test_post_solution_file(self):
        import tempfile
        import os
        filename = tempfile.mkstemp()[1]
        f = open(filename, 'w')
        f.write('These are the file contents')
        f.close()
        f = open(filename, 'r')
        post_data = {'file': f}
        response = self.client.post(self.solution.get_absolute_url()+'add_solution_file/', post_data,
                                    follow=True)
        f.close()
        os.remove(filename)
        self.assertTemplateUsed(response, 'tests/solution_detail.html')
        self.assertContains(response, os.path.basename(filename))
    

While this test works just fine, it leaves the uploaded file in the media directory after finishing. Of course, the deletion could be taken care of in tearDown(), but I was wondering if Django had another way of dealing with this.

One solution I was thinking of was using a different media folder for tests which must be kept synced with the test fixtures. Is there any way to specify another media directory in settings.py when tests are being run? And can I include some sort of hook to dumpdata so that it syncs the files in the media folders?

So, is there a more Pythonic or Django-specific way of dealing with unit tests involving files?

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

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

发布评论

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

评论(5

浪漫之都 2024-08-29 00:03:00

Django 提供了一种在 FileFields 上编写测试的好方法,而无需在实际文件系统中进行修改 - 使用 SimpleUploadedFile。

from django.core.files.uploadedfile import SimpleUploadedFile

my_model.file_field = SimpleUploadedFile('best_file_eva.txt', b'these are the contents of the txt file')

这是 django 的神奇功能之一,但在文档中却没有出现:)。然而,它被引用这里

Django provides a great way to write tests on FileFields without mucking about in the real filesystem - use a SimpleUploadedFile.

from django.core.files.uploadedfile import SimpleUploadedFile

my_model.file_field = SimpleUploadedFile('best_file_eva.txt', b'these are the contents of the txt file')

It's one of django's magical features-that-don't-show-up-in-the-docs :). However it is referred to here.

橘味果▽酱 2024-08-29 00:03:00

您可以使用 @override_settings() 装饰器覆盖测试的 MEDIA_ROOT 设置如文档所述

from django.test import override_settings


@override_settings(MEDIA_ROOT='/tmp/django_test')
def test_post_solution_file(self):
  # your code here

You can override the MEDIA_ROOT setting for your tests using the @override_settings() decorator as documented:

from django.test import override_settings


@override_settings(MEDIA_ROOT='/tmp/django_test')
def test_post_solution_file(self):
  # your code here
昔日梦未散 2024-08-29 00:03:00

我之前为整个图库应用程序编写过单元测试,对我来说效果好的方法是使用 python tempfile 和 Shutil 模块在临时目录中创建测试文件的副本,然后将它们全部删除。

以下示例不工作/不完整,但应该让您走上正确的道路:

import os, shutil, tempfile

PATH_TEMP = tempfile.mkdtemp(dir=os.path.join(MY_PATH, 'temp'))

def make_objects():
    filenames = os.listdir(TEST_FILES_DIR)

    if not os.access(PATH_TEMP, os.F_OK):
        os.makedirs(PATH_TEMP)

    for filename in filenames:
        name, extension = os.path.splitext(filename)
        new = os.path.join(PATH_TEMP, filename)
        shutil.copyfile(os.path.join(TEST_FILES_DIR, filename), new)

        #Do something with the files/FileField here

def remove_objects():
    shutil.rmtree(PATH_TEMP)

我在单元测试的 setUp() 和 TeaDown() 方法中运行这些方法,效果非常好!您已经获得了文件的干净副本来测试可重用且可预测的文件字段。

I've written unit tests for an entire gallery app before, and what worked well for me was using the python tempfile and shutil modules to create copies of the test files in temporary directories and then delete them all afterwards.

The following example is not working/complete, but should get you on the right path:

import os, shutil, tempfile

PATH_TEMP = tempfile.mkdtemp(dir=os.path.join(MY_PATH, 'temp'))

def make_objects():
    filenames = os.listdir(TEST_FILES_DIR)

    if not os.access(PATH_TEMP, os.F_OK):
        os.makedirs(PATH_TEMP)

    for filename in filenames:
        name, extension = os.path.splitext(filename)
        new = os.path.join(PATH_TEMP, filename)
        shutil.copyfile(os.path.join(TEST_FILES_DIR, filename), new)

        #Do something with the files/FileField here

def remove_objects():
    shutil.rmtree(PATH_TEMP)

I run those methods in the setUp() and tearDown() methods of my unit tests and it works great! You've got a clean copy of your files to test your filefield that are reusable and predictable.

帅冕 2024-08-29 00:03:00

使用 pytest 和 pytest-django,我在 conftest.py 文件中使用它:

import tempfile
import shutil
from pytest_django.lazy_django import skip_if_no_django
from pytest_django.fixtures import SettingsWrapper


@pytest.fixture(scope='session')
#@pytest.yield_fixture()
def settings():
    """A Django settings object which restores changes after the testrun"""
    skip_if_no_django()

    wrapper = SettingsWrapper()
    yield wrapper
    wrapper.finalize()


@pytest.fixture(autouse=True, scope='session')
def media_root(settings):
    tmp_dir = tempfile.mkdtemp()
    settings.MEDIA_ROOT = tmp_dir
    yield settings.MEDIA_ROOT
    shutil.rmtree(tmp_dir)


@pytest.fixture(scope='session')
def django_db_setup(media_root, django_db_setup):
    print('inject_after')

可能会有所帮助:

  1. https://dev.funkwhale.audio/funkwhale/funkwhale/blob/de777764da0c0e9fe66d0bb76317679be964588b/api/tests/conftest.py
  2. https://framagit.org/ideascube/ideascube/blob/master/conftest.py
  3. < a href="https://stackoverflow.com/a/56177770/5305401">https://stackoverflow.com/a/56177770/5305401

with pytest and pytest-django, I use this in conftest.py file:

import tempfile
import shutil
from pytest_django.lazy_django import skip_if_no_django
from pytest_django.fixtures import SettingsWrapper


@pytest.fixture(scope='session')
#@pytest.yield_fixture()
def settings():
    """A Django settings object which restores changes after the testrun"""
    skip_if_no_django()

    wrapper = SettingsWrapper()
    yield wrapper
    wrapper.finalize()


@pytest.fixture(autouse=True, scope='session')
def media_root(settings):
    tmp_dir = tempfile.mkdtemp()
    settings.MEDIA_ROOT = tmp_dir
    yield settings.MEDIA_ROOT
    shutil.rmtree(tmp_dir)


@pytest.fixture(scope='session')
def django_db_setup(media_root, django_db_setup):
    print('inject_after')

might be helpful:

  1. https://dev.funkwhale.audio/funkwhale/funkwhale/blob/de777764da0c0e9fe66d0bb76317679be964588b/api/tests/conftest.py
  2. https://framagit.org/ideascube/ideascube/blob/master/conftest.py
  3. https://stackoverflow.com/a/56177770/5305401
余罪 2024-08-29 00:03:00

这就是我为测试所做的。上传文件后,它应该最终出现在我的组织模型对象的照片属性中:

    import tempfile
    filename = tempfile.mkstemp()[1]
    f = open(filename, 'w')
    f.write('These are the file contents')
    f.close()
    f = open(filename, 'r')
    post_data = {'file': f}
    response = self.client.post("/org/%d/photo" % new_org_data["id"], post_data)
    f.close()
    self.assertEqual(response.status_code, 200)

    ## Check the file
    ## org is where the file should end up
    org = models.Organization.objects.get(pk=new_org_data["id"])
    self.assertEqual("These are the file contents", org.photo.file.read())

    ## Remove the file
    import os
    os.remove(org.photo.path)

This is what I did for my test. After uploading the file it should end up in the photo property of my organization model object:

    import tempfile
    filename = tempfile.mkstemp()[1]
    f = open(filename, 'w')
    f.write('These are the file contents')
    f.close()
    f = open(filename, 'r')
    post_data = {'file': f}
    response = self.client.post("/org/%d/photo" % new_org_data["id"], post_data)
    f.close()
    self.assertEqual(response.status_code, 200)

    ## Check the file
    ## org is where the file should end up
    org = models.Organization.objects.get(pk=new_org_data["id"])
    self.assertEqual("These are the file contents", org.photo.file.read())

    ## Remove the file
    import os
    os.remove(org.photo.path)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文