Тест Django FileField с использованием тестовых приборов
Я пытаюсь построить тесты для некоторых моделей, имеющих FileField. Модель выглядит следующим образом:
class SolutionFile(models.Model):
'''
A file from a solution.
'''
solution = models.ForeignKey(Solution)
file = models.FileField(upload_to=make_solution_file_path)
Я столкнулся с двумя проблемами:
-
При сохранении данных на прибор с помощью ./manage.py dumpdata
содержимое файла не сохраняется, только имя файла сохраняется в приборе. Хотя я считаю, что это ожидаемое поведение, поскольку содержимое файла не сохраняется в базе данных, я хотел бы как-то включить эту информацию в прибор для тестирования.
-
У меня есть тестовый пример для загрузки файла, который выглядит так:
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, чтобы она синхронизировала файлы в папках с носителями?
Итак, существует ли более питоновский или Django-специфический способ решения модульных тестов с участием файлов?
Ответы
Ответ 1
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, которые не появляются в документах :). Однако он упоминается здесь как here.
Ответ 2
Вы можете переопределить параметр MEDIA_ROOT
для своих тестов с помощью @override_settings()
decorator как описано в документе:
from django.test import override_settings
@override_settings(MEDIA_ROOT='/tmp/django_test')
def test_post_solution_file(self):
# your code here
Ответ 3
Я уже писал единичные тесты для всего приложения галереи, и то, что сработало для меня, было использование модулей tempfile и shutil для python для создания копий тестовых файлов во временных каталогах, а затем впоследствии удалить их.
Следующий пример не работает/завершен, но должен привести вас к правильному пути:
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() моих модульных тестов, и он отлично работает! У вас есть чистая копия ваших файлов, чтобы проверить ваше файловое поле, которое можно использовать повторно и предсказуемо.
Ответ 4
Это то, что я сделал для своего теста. После того, как вы загрузили файл, он должен закончиться в качестве свойства фотографии моего объекта модели организации:
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)