如何在 django 单元测试文件上传

在我的 django 应用程序中,我有一个完成文件上传的视图

...
if  (request.method == 'POST'):
if request.FILES.has_key('file'):
file = request.FILES['file']
with open(settings.destfolder+'/%s' % file.name, 'wb+') as dest:
for chunk in file.chunks():
dest.write(chunk)

我想对视图进行单元测试。我计划测试快乐路径和失败路径。.即 request.FILES没有密钥‘ file’的情况,即 request.FILES['file']None的情况。.

如何设置快乐路径的后期数据? 有人能告诉我吗?

80969 次浏览

来自 Client.post台的姜戈文档:

提交文件是一种特殊情况 提供文件字段名作为键,以及文件的文件句柄 你希望上载为一个值,例如:

c = Client()
with open('wishlist.doc') as fp:
c.post('/customers/wishes/', {'name': 'fred', 'attachment': fp})

我建议你看看姜戈 RequestFactory。这是模拟请求中提供的数据的最佳方法。

这么说吧,我在你的代码中发现了一些缺陷。

  • “单元”测试意味着测试 就一个“单元”功能, 如果你想测试这个视图,你需要测试这个视图和文件 系统,因此,不是真正的单元测试。为了使这一点更清楚。如果 你运行那个测试,视图运行良好,但是你没有 保存该文件的权限,您的测试将因此失败。
  • 其他重要的事情是 测试速度。如果你正在做类似于 TDD 测试的执行速度非常重要。 访问任何 I/O 都不是一个好主意。

因此,我建议你在 重构中使用如下函数:

def upload_file_to_location(request, location=None): # Can use the default configured

你可以用 巨蟒模仿秀来嘲笑一下。

PS: 你也可以使用 Django 测试客户端,但是这意味着你要添加更多的东西来测试,因为客户端使用了 Sessions、中间件等等。与单元测试完全不同。

我为自己的事件相关应用程序做了类似的工作,但是您应该有足够多的代码来处理自己的用例

import tempfile, csv, os


class UploadPaperTest(TestCase):


def generate_file(self):
try:
myfile = open('test.csv', 'wb')
wr = csv.writer(myfile)
wr.writerow(('Paper ID','Paper Title', 'Authors'))
wr.writerow(('1','Title1', 'Author1'))
wr.writerow(('2','Title2', 'Author2'))
wr.writerow(('3','Title3', 'Author3'))
finally:
myfile.close()


return myfile


def setUp(self):
self.user = create_fuser()
self.profile = ProfileFactory(user=self.user)
self.event = EventFactory()
self.client = Client()
self.module = ModuleFactory()
self.event_module = EventModule.objects.get_or_create(event=self.event,
module=self.module)[0]
add_to_admin(self.event, self.user)


def test_paper_upload(self):
response = self.client.login(username=self.user.email, password='foz')
self.assertTrue(response)


myfile = self.generate_file()
file_path = myfile.name
f = open(file_path, "r")


url = reverse('registration_upload_papers', args=[self.event.slug])


# post wrong data type
post_data = {'uploaded_file': i}
response = self.client.post(url, post_data)
self.assertContains(response, 'File type is not supported.')


post_data['uploaded_file'] = f
response = self.client.post(url, post_data)


import_file = SubmissionImportFile.objects.all()[0]
self.assertEqual(SubmissionImportFile.objects.all().count(), 1)
#self.assertEqual(import_file.uploaded_file.name, 'files/registration/{0}'.format(file_path))


os.remove(myfile.name)
file_path = import_file.uploaded_file.path
os.remove(file_path)

我曾经做过相同的 with open('some_file.txt') as fp:,但是后来我需要图片,视频和其他真实的文件在回购中,而且我正在测试一部分的 Django 核心组件,是很好的测试,所以现在这是我一直在做的:

from django.core.files.uploadedfile import SimpleUploadedFile


def test_upload_video(self):
video = SimpleUploadedFile("file.mp4", "file_content", content_type="video/mp4")
self.client.post(reverse('app:some_view'), {'video': video})
# some important assertions ...

Python 3.5 + 中你需要使用 bytes对象而不是 str。将 "file_content"改为 b"file_content"

它一直运行良好,SimpleUploadedFile创建了一个 InMemoryFile,其行为类似于一个常规的上传,您可以选择的名称,内容和内容类型。

在 Django 1.7中,TestCase 存在一个问题,可以通过使用 open (filepath,‘ rb’)来解决,但是当使用测试客户机时,我们无法控制它。我认为最好确保 file.read ()总是返回字节。

来源: https://code.djangoproject.com/ticket/23912,作者: KevinEtienne

如果没有 rb 选项,则会引发 TypeError:

TypeError: sequence item 4: expected bytes, bytearray, or an object with the buffer interface, str found

我做过类似的事情:

from django.core.files.uploadedfile import SimpleUploadedFile
from django.test import TestCase
from django.core.urlresolvers import reverse
from django.core.files import File
from django.utils.six import BytesIO


from .forms import UploadImageForm


from PIL import Image
from io import StringIO




def create_image(storage, filename, size=(100, 100), image_mode='RGB', image_format='PNG'):
"""
Generate a test image, returning the filename that it was saved as.


If ``storage`` is ``None``, the BytesIO containing the image data
will be passed instead.
"""
data = BytesIO()
Image.new(image_mode, size).save(data, image_format)
data.seek(0)
if not storage:
return data
image_file = ContentFile(data.read())
return storage.save(filename, image_file)




class UploadImageTests(TestCase):
def setUp(self):
super(UploadImageTests, self).setUp()




def test_valid_form(self):
'''
valid post data should redirect
The expected behavior is to show the image
'''
url = reverse('image')
avatar = create_image(None, 'avatar.png')
avatar_file = SimpleUploadedFile('front.png', avatar.getvalue())
data = {'image': avatar_file}
response = self.client.post(url, data, follow=True)
image_src = response.context.get('image_src')


self.assertEquals(response.status_code, 200)
self.assertTrue(image_src)
self.assertTemplateUsed('content_upload/result_image.html')

Create _ image 函数将创建图像,因此不需要给出图像的静态路径。

注意: 您可以根据您的代码更新代码。 Python 3.6的代码。

from rest_framework.test import force_authenticate
from rest_framework.test import APIRequestFactory


factory = APIRequestFactory()
user = User.objects.get(username='#####')
view = <your_view_name>.as_view()
with open('<file_name>.pdf', 'rb') as fp:
request=factory.post('<url_path>',{'file_name':fp})
force_authenticate(request, user)
response = view(request)

正如 姜戈的官方文件中提到的:

提交文件是一种特殊情况。要发布文件,您只需提供文件字段名作为键,以及要上载的文件的文件句柄作为值。例如:

c = Client()
with open('wishlist.doc') as fp:
c.post('/customers/wishes/', {'name': 'fred', 'attachment': fp})

更多信息: 如何检查文件是否作为参数传递给某个函数?

在测试时,有时我们希望确保将文件作为参数传递给某个函数。

例如:。

...
class AnyView(CreateView):
...
def post(self, request, *args, **kwargs):
attachment = request.FILES['attachment']
# pass the file as an argument
my_function(attachment)
...

在测试中,使用如下 巨蟒的嘲弄:

# Mock 'my_function' and then check the following:


response = do_a_post_request()


self.assertEqual(mock_my_function.call_count, 1)
self.assertEqual(
mock_my_function.call_args,
call(response.wsgi_request.FILES['attachment']),
)
from django.test import Client
from requests import Response


client = Client()
with open(template_path, 'rb') as f:
file = SimpleUploadedFile('Name of the django file', f.read())
response: Response = client.post(url, format='multipart', data={'file': file})

希望这个能帮上忙。

我使用的是 Python = = 3.8.2,Django = = 3.0.4,djangorestFramework = = 3.11.0

我试过 self.client.post但是 Resolver404异常。

下面这句话对我很有用:

import requests
upload_url='www.some.com/oaisjdoasjd' # your url to upload
with open('/home/xyz/video1.webm', 'rb') as video_file:
# if it was a text file we would perhaps do
# file = video_file.read()
response_upload = requests.put(
upload_url,
data=video_file,
headers={'content-type': 'video/webm'}
)

如果您想添加其他数据与文件上传,然后按照下面的方法

file = open('path/to/file.txt', 'r', encoding='utf-8')

    data = {
'file_name_to_receive_on_backend': file,
'param1': 1,
'param2': 2,
.
.
}


response = self.client.post("/url/to/view", data, format='multipart')`

唯一的 file_name_to_receive_on_backend将作为文件接收,其他参数通常作为 post paramas 接收。

我正在使用 django 休息框架,我必须测试多个文件的上传。

最后,我通过在 APIClient.post请求中使用 format="multipart"来获得它。

from rest_framework.test import APIClient
...
self.client = APIClient()
with open('./photo.jpg', 'rb') as fp:
resp = self.client.post('/upload/',
{'images': [fp]},
format="multipart")

我正在使用 GraphQL,上传测试:

with open('test.jpg', 'rb') as fp:
response = self.client.execute(query, variables, data={'image': [fp]})

类变异代码

@classmethod
def mutate(cls, root, info, **kwargs):
if image := info.context.FILES.get("image", None):
kwargs["image"] = image
TestingMainModel.objects.get_or_create(
id=kwargs["id"],
defaults=kwargs
)

非常方便的解决方案与 嘲笑

from django.test import TestCase, override_settings
#use your own client request factory
from my_framework.test import APIClient


from django.core.files import File
import tempfile
from pathlib import Path
import mock


image_mock = mock.MagicMock(spec=File)
image_mock.name = 'image.png' # or smt else


class MyTest(TestCase):


# I assume we want to put this file in storage
# so to avoid putting garbage in our MEDIA_ROOT
# we're using temporary storage for test purposes
@override_settings(MEDIA_ROOT=Path(tempfile.gettempdir()))
def test_send_file(self):
client = APIClient()
client.post(
'/endpoint/'
{'file':image_mock},
format="multipart"
)