我正在尝试为一些具有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中包含某种钩子,以便同步媒体文件夹中的文件吗?
那么,是否有更多Pythonic或Django特定方式处理涉及文件的单元测试?
答案 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')
可能会有所帮助:
答案 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)