Django使用测试夹具测试FileField

时间:2010-02-15 14:23:07

标签: python django unit-testing django-testing filefield

我正在尝试为一些具有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))
    
  3. 虽然此测试工作正常,但在完成后将上传的文件保留在媒体目录中。当然,删除可以在tearDown()中处理,但我想知道Django是否有另一种处理方式。

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

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

5 个答案:

答案 0 :(得分:21)

Django提供了一种在FileFields上编写测试的好方法,而不会在真正的文件系统中乱码 - 使用SimpleUploadedFile。

from django.core.files.uploadedfile import SimpleUploadedFile

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

这是django的魔法特征之一 - 不要在文档中显示:)。但是,它被称为here

答案 1 :(得分:5)

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

from django.test import override_settings


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

答案 2 :(得分:3)

之前我已经为整个图库应用编写了单元测试,对我来说效果很好的是使用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()和tearDown()方法中运行这些方法,效果很好!你有一个干净的文件副本来测试你的文件字段是可重用和可预测的。

答案 3 :(得分: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

答案 4 :(得分:0)

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

    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)