当前位置: 首页 > 知识库问答 >
问题:

如何使用django rest framework的测试客户端测试二进制文件上传?

田鹤轩
2023-03-14

我有一个Django应用程序,其视图接受要上传的文件。使用Django REST框架,我对APIView进行子类化并实现post()方法,如下所示:

class FileUpload(APIView):
    permission_classes = (IsAuthenticated,)

    def post(self, request, *args, **kwargs):
        try:
            image = request.FILES['image']
            # Image processing here.
            return Response(status=status.HTTP_201_CREATED)
        except KeyError:
            return Response(status=status.HTTP_400_BAD_REQUEST, data={'detail' : 'Expected image.'})

现在,我正在尝试编写几个单元测试,以确保需要身份验证,并实际处理上传的文件。

class TestFileUpload(APITestCase):
    def test_that_authentication_is_required(self):
        self.assertEqual(self.client.post('my_url').status_code, status.HTTP_401_UNAUTHORIZED)

    def test_file_is_accepted(self):
        self.client.force_authenticate(self.user)
        image = Image.new('RGB', (100, 100))
        tmp_file = tempfile.NamedTemporaryFile(suffix='.jpg')
        image.save(tmp_file)
        with open(tmp_file.name, 'rb') as data:
            response = self.client.post('my_url', {'image': data}, format='multipart')
            self.assertEqual(status.HTTP_201_CREATED, response.status_code)

但是当REST框架试图编码请求时,这将失败

Traceback (most recent call last):
  File "/home/vagrant/.virtualenvs/myapp/lib/python3.3/site-packages/django/utils/encoding.py", line 104, in force_text
    s = six.text_type(s, encoding, errors)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 118: invalid start byte

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/vagrant/webapp/myproject/myapp/tests.py", line 31, in test_that_jpeg_image_is_accepted
    response = self.client.post('my_url', { 'image': data}, format='multipart')
  File "/home/vagrant/.virtualenvs/myapp/lib/python3.3/site-    packages/rest_framework/test.py", line 76, in post
    return self.generic('POST', path, data, content_type, **extra)
  File "/home/vagrant/.virtualenvs/myapp/lib/python3.3/site-packages/rest_framework/compat.py", line 470, in generic
    data = force_bytes_or_smart_bytes(data, settings.DEFAULT_CHARSET)
  File "/home/vagrant/.virtualenvs/myapp/lib/python3.3/site-packages/django/utils/encoding.py", line 73, in smart_text
    return force_text(s, encoding, strings_only, errors)
  File "/home/vagrant/.virtualenvs/myapp/lib/python3.3/site-packages/django/utils/encoding.py", line 116, in force_text
    raise DjangoUnicodeDecodeError(s, *e.args)
django.utils.encoding.DjangoUnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 118: invalid start byte. You passed in b'--BoUnDaRyStRiNg\r\nContent-Disposition: form-data; name="image"; filename="tmpyz2wac.jpg"\r\nContent-Type: image/jpeg\r\n\r\n\xff\xd8\xff[binary data omitted]' (<class 'bytes'>)

如何让测试客户端发送数据而不尝试将其解码为UTF-8?

共有3个答案

罗允晨
2023-03-14

您可以使用Django内置的SimpleUploadedFile:

from django.core.files.uploadedfile import SimpleUploadedFile

class TestFileUpload(APITestCase):
...

    def test_file_is_accepted(self):
        ...

       tmp_file = SimpleUploadedFile(
                      "file.jpg", "file_content", content_type="image/jpg")

       response = self.client.post(
                      'my_url', {'image': tmp_file}, format='multipart')
       self.assertEqual(response.status_code, status.HTTP_201_CREATED)

尹善
2023-03-14

Python3用户:确保mode='rb'(读取,二进制)打开文件。否则,当Django调用文件上的read时,utf-8编解码器将立即开始阻塞。文件应解码为二进制,而不是utf-8、ascii或任何其他编码。

# This won't work in Python 3
with open(tmp_file.name) as fp:
        response = self.client.post('my_url', 
                                   {'image': fp}, 
                                   format='multipart')

# Set the mode to binary and read so it can be decoded as binary
with open(tmp_file.name, 'rb') as fp:
        response = self.client.post('my_url', 
                                   {'image': fp}, 
                                   format='multipart')

公瑞
2023-03-14

在测试文件上载时,应该将流对象传递到请求中,而不是数据中。

@arocks在评论中指出了这一点

改为传递{'Image': file}

但这并不能完全解释为什么需要它(也不符合问题)。对于这个特定的问题,你应该做

from PIL import Image

class TestFileUpload(APITestCase):

    def test_file_is_accepted(self):
        self.client.force_authenticate(self.user)

        image = Image.new('RGB', (100, 100))

        tmp_file = tempfile.NamedTemporaryFile(suffix='.jpg')
        image.save(tmp_file)
        tmp_file.seek(0)

        response = self.client.post('my_url', {'image': tmp_file}, format='multipart')

       self.assertEqual(status.HTTP_201_CREATED, response.status_code)

这将匹配一个标准的Django请求,其中文件作为流对象传入,Django REST框架处理它。当您只是传入文件数据时,Django和Django REST框架会将其解释为字符串,这会导致问题,因为它期待一个流。

对于那些来这里寻找另一个常见错误的人来说,为什么文件上传不起作用,但正常的表单数据会起作用:在创建请求时,请确保设置form="Multipart"

这也带来了类似的问题,@RobinElvin在评论中指出了这一点

这是因为我错过了格式='多部分'

 类似资料:
  • 客户端测试更多关心客户端方面的代码执行情况,通常是web浏览器或者浏览器插件。区别于服务器端,代码在客户端执行并直接返回随后的结果。 下列文章描述了如何进行客户端的web应用测试: 基于DOM跨站脚本测试 (OTG-CLIENT-001) JavaScript脚本执行测试 (OTG-CLIENT-002) HTML注入测试 (OTG-CLIENT-003) 客户端URL重定向测试 (OTG-CLI

  • 首先,Cowboy和Websockets的文档确实很缺乏,但一般来说,一旦破译,使用它是非常好的。然后将这些信息从Erlang传输到Elixir是另一个步骤。多亏了7Stud的这篇文章,我得以让一个功能正常的websocket用于测试目的,但我无法让它同时监听和可选地发送消息。我认为这是因为receive阻塞了发送所需的线程,而这与websocket连接有内在的联系,所以它在等待接收时不能发送。也

  • 我使用Retrofit为我的REST API实现了一个Java客户端。客户端是一个简单的java应用程序,而不是Android应用程序。如何为我的Java客户端编写单元测试?以下是我的服务类之一。

  • 9.16快手客户端测试实习二面(49min) 自我介绍 手撕代码:数组倒数第k大的数 讲思路 对于测试的理解 TCP和UDP的区别 TCP和UDP的应用场景 快手的短视频使用的是TCP还是UDP 使用过GIT吗 假如上传代码的时候产生冲突要如何解决 知道哪些自动化测试的工具和方法 requests库用到过哪些函数 你觉得什么事是你排除万难,最终成功,感觉有很大成就的事 你觉得自己的缺点是什么 反问

  • 我正在使用Quarkus中的MicroProfile REST客户端,想知道如何对自定义客户端接口进行单元测试? 示例服务: 是否可以编写包含此功能的单元测试?例如。我想测试请求主体是否得到了正确的处理,是否包含了正确的JSON(特别是因为我遇到了JVM和本机映像模式之间行为不同的问题)。 REST服务器资源可以很容易地用REST保证测试,但是我没有发现REST客户机接口有类似的情况。 有什么建议

  • 我们实际上使用了JUnit和FakeSftpServerRule来测试我们定制的SFTP客户端。效果很好。 最后,我们希望摆脱junit,转而使用spock框架,因为我们试图迁移到groovy。 你们知道FakeSftpServerRule的等价物吗?或者,你们知道把junit规则“转换”成spock规则等价物的方法吗? 非常感谢。