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

Хотя этот тест работает нормально, после завершения он оставляет загруженный файл в каталоге мультимедиа. Конечно, об удалении можно было бы позаботиться в tearDown(), но мне было интересно, есть ли у Django другой способ справиться с этим.

Одно из решений, о котором я подумал, заключалось в использовании другой папки мультимедиа для тестов, которые необходимо синхронизировать с тестовыми приборами. Есть ли способ указать другой каталог мультимедиа в settings.py во время выполнения тестов? И могу ли я включить какой-нибудь перехватчик для dumpdata, чтобы он синхронизировал файлы в папках мультимедиа?

Итак, есть ли более специфичный для Pythonic или Django способ работы с модульными тестами, включающими файлы?


person sttwister    schedule 15.02.2010    source источник
comment
Итак, os.remove (foo) не работает? Выдает ли исключение? Возможно, в этом каталоге / файле нет правильных привилегий, чтобы удалить его изнутри вашего модульного теста?   -  person Steve Jalim    schedule 15.02.2010
comment
Часть os.remove() в коде удаляет файл из временного каталога. Чтобы удалить загруженный файл, мне пришлось бы заглянуть в каталог мультимедиа и следовать более сложной логике, чтобы найти точное местоположение файла. Я ищу более простой автоматизированный способ сделать это, если он вообще существует.   -  person sttwister    schedule 15.02.2010
comment
Ой, извини! Я неправильно прочитал твой пост. Как насчет взлома settings.MEDIA_ROOT = '/ path / to / project / static / and / then / Alternative / storage /' и settings.MEDIA_URL = '/ static / и / then / alternate / storage /' в вашем setUp для ваших тестов ? Хакер, но со своей работой справился ...   -  person Steve Jalim    schedule 16.02.2010
comment
Можно ли написать кастомную реализацию хранилища и заменить ее во время тестов? Похоже, что должно быть достаточно легко написать реализацию, которая просто хранит загруженные файлы в памяти. Я не уверен в том, как заменить его во время тестирования.   -  person Weeble    schedule 08.04.2010


Ответы (5)


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 - которые не появляются в документации :). Однако здесь имеется ссылка на .

person waterproof    schedule 11.12.2013
comment
Недвоичное содержимое вызывает ошибку в Python 3+; вы можете исправить это, просто сделав содержимое двоичным, например: my_model.file_field = SimpleUploadedFile('best_file_eva.txt', b'these are the file contents!') - person LaundroMat; 02.07.2016

Вы можете переопределить настройку 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
person w00kie    schedule 10.01.2014

Раньше я писал модульные тесты для всего приложения галереи, и что мне понравилось, так это использование модулей 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 () своих модульных тестов, и они отлично работают! У вас есть чистая копия ваших файлов, чтобы протестировать ваше файловое поле, которое можно использовать повторно и предсказуемо.

person Gabriel Hurley    schedule 15.02.2010
comment
Я не понимаю, как это может мне помочь. Я хочу заменить каталог мультимедиа Django тестовым. А еще хочу как-то экспортировать / копировать файлы при использовании ./manage.py dumpdata. - person sttwister; 16.02.2010
comment
Перезапись медиа-каталога django - плохая идея. Если вы не переместите текущий каталог мультимедиа в другое место и не вернете его позже, вы никогда не сможете запускать тесты на своем действующем сайте, потому что это будет разрушительная операция. Вы можете переместить папку мультимедиа и вернуть ее с помощью shutils, как я предлагал выше. Кроме того, dumpdata никогда не будет экспортировать файлы за вас. Для этого вам придется написать свой собственный скрипт или расширение manage.py. - person Gabriel Hurley; 17.02.2010

с 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
person javed    schedule 10.09.2020

Это то, что я сделал для своего теста. После загрузки файла он должен оказаться в свойстве 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)
person Gustavo Ambrozio    schedule 04.05.2012