28

我正在尝试为某些具有 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,不保存文件内容,仅将文件名保存到夹具中。虽然我发现这是预期的行为,因为文件内容没有保存到数据库中,但我想以某种方式将此信息包含在夹具中以进行测试。

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

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

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

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

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

4

5 回答 5

30

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 的神奇功能之一——在文档中不会出现 :)。不过这里提到了。

于 2013-12-11T01:10:01.557 回答
5

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

from django.test import override_settings


@override_settings(MEDIA_ROOT='/tmp/django_test')
def test_post_solution_file(self):
  # your code here
于 2014-01-10T05:44:49.747 回答
3

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.

于 2010-02-15T22:14:23.020 回答
1

使用 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. https://stackoverflow.com/a/56177770/5305401
于 2020-09-10T06:12:00.257 回答
0

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

    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)
于 2012-05-04T20:12:29.367 回答