当pytest与REST框架交互时,PATCH和PUT不按预期工作

15

我正在使用Django REST框架构建API。

为了测试这个API,我正在使用pytest和测试客户端,像这样:

def test_doesnt_find(self, client):
    resp = client.post(self.url, data={'name': '123'})
    assert resp.status_code == 404
或者
def test_doesnt_find(self, client):
    resp = client.get(self.url, data={'name': '123'})
    assert resp.status_code == 404

当使用REST框架的通用GET、POST和DELETE类(如DestroyAPIViewRetrieveUpdateAPIView或只是使用get和post函数的APIView)时,两者都起作用。

我遇到问题的地方是使用PATCH和PUT视图,例如RetrieveUpdateAPIView。在这里,我突然需要使用:

resp = client.patch(self.url, data="name=123", content_type='application/x-www-form-urlencoded')
或者
resp = client.patch(self.url, data=json.dumps({'name': '123'}), content_type='application/json')

如果我像往常一样尝试使用测试客户端,我会遇到错误:

rest_framework.exceptions.UnsupportedMediaType: Unsupported media type "application/octet-stream" in request.

当我在client.patch()调用中指定'application/json'时:

rest_framework.exceptions.ParseError: JSON parse error - Expecting property name enclosed in double quotes: line 1 column 2 (char 1)`

有人能向我解释这种行为吗?它很难捕捉,因为curl只需使用-X PATCH -d"name=123"就可以正常工作。


2
你所指的测试客户端是什么?Django的?DRF的?还是其他的? - Linovia
这是Django测试客户端[http://pytest-django.readthedocs.io/en/latest/helpers.html#client-django-test-client]。 - David Schumann
4个回答

23
rest_framework.exceptions.ParseError: JSON解析错误 - 期望在双引号中括起来的属性名:第1行第2列(字符1)。 通常这表示您在json中发送了一个字符串内的字符串。 例如:
resp = client.patch(self.url, data=json.dumps("name=123"), content_type='application/json')

会导致这种问题。

rest_framework.exceptions.UnsupportedMediaType: 请求中不支持的媒体类型 "application/octet-stream"。

这意味着请求已被发送为 "application/octet-stream",这是 Django 测试默认值。

为了方便处理所有这些问题,Django REST 框架提供了自己的客户端:http://www.django-rest-framework.org/api-guide/testing/#apiclient

请注意,语法与 Django 的略有不同,并且您无需处理 json 编码。


7
建议:您可以继承APITestCase而不是实例化APIClient来编写测试类。这样做可以简化您的代码。 - Daniel
2
resp = client.patch(self.url, json={"name": 123}) - Ammad Khalid

3

Pytest使用Django测试客户端和client.post默认的content_type是multipart/form-data,而putpatchdelete使用application/octet-stream

因此,有时候这很棘手。即使使用post请求,如果您计划支持JSON负载,也必须在测试请求中告知内容类型。无论如何,使用最近的Django版本,您只需将数据对象传递给客户端请求,它就会为您序列化,正如文档中所声明的那样:

如果您提供content_type为application/json,则如果它是字典、列表或元组,数据将使用json.dumps()进行序列化。默认情况下,使用DjangoJSONEncoder进行序列化,可以通过向Client提供json_encoder参数来覆盖此行为。这种序列化也适用于put()、patch()和delete()请求。

例如:

resp = client.patch(self.url, {'name': '123'}, content_type='application/json')

0
关于您收到的带有JSON数据的请求,由于JSON语法需要在字符串上使用双引号,因此出现了此错误。

0
来晚了。但对于谷歌用户,你应该在Django测试客户端中使用format='json'
手动进行json.dump和设置content_type并不总是按预期工作。
resp = client.patch(self.url, data={'name': '123'}, format='json')

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接