I am building an API using the django REST framework.
To test this API I am using pytest and the test client like so:
def test_doesnt_find(self, client):
resp = client.post(self.url, data={'name': '123'})
assert resp.status_code == 404
or
def test_doesnt_find(self, client):
resp = client.get(self.url, data={'name': '123'})
assert resp.status_code == 404
both work when using the general GET, POST and DELETE Classes of the REST framework (like DestroyAPIView
, RetrieveUpdateAPIView
or just APIView
using get and post functions)
Where I get problems is when using PATCH and PUT views. Such as RetrieveUpdateAPIView
. Here I suddenly have to use:
resp = client.patch(self.url, data="name=123", content_type='application/x-www-form-urlencoded')
or
resp = client.patch(self.url, data=json.dumps({'name': '123'}), content_type='application/json')
If I simply try to use the test client like I am used to, I get errors:
rest_framework.exceptions.UnsupportedMediaType: Unsupported media type "application/octet-stream" in request.
And when I specify 'application/json' in the client.patch() call:
rest_framework.exceptions.ParseError: JSON parse error - Expecting property name enclosed in double quotes: line 1 column 2 (char 1)`
Can anyone explain this behavior to me? It is especially hard to catch as curl simply works as well using -X PATCH -d"name=123"
.
Pytest uses the django test client and client.post default content_type is multipart/form-data, while put, patch and delete use application/octet-stream.
That's why this is tricky sometimes. Even with post requests, if you plan to support JSON payload, you must tell the content type in the test request. Anyway, with recent Django versions, you can just pass the data object to the client request and it will be serialized for you, as declared in the docs:
If you provide content_type as application/json, the data is serialized using json.dumps() if it’s a dict, list, or tuple. Serialization is performed with DjangoJSONEncoder by default, and can be overridden by providing a json_encoder argument to Client. This serialization also happens for put(), patch(), and delete() requests.
For example:
resp = client.patch(self.url, {'name': '123'}, content_type='application/json')
rest_framework.exceptions.ParseError: JSON parse error - Expecting property name enclosed in double quotes: line 1 column 2 (char 1)`
This is usually sign that you send a string inside a string in json. For example:
resp = client.patch(self.url, data=json.dumps("name=123"), content_type='application/json')
will cause this kind of issues.
rest_framework.exceptions.UnsupportedMediaType: Unsupported media type "application/octet-stream" in request.
This means that the request has been sent as "application/octet-stream" which is Django's test default.
To ease the pain with dealing with all that, Django REST framework provides a client on its own: http://www.django-rest-framework.org/api-guide/testing/#apiclient
Note that the syntax is slightly different from Django's one and that you won't have to deal with json encoding.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With