34

I'm using Python 3.4 and Django 1.7. I have a view returning JsonResponse.

def add_item_to_collection(request):
    #(...)
    return JsonResponse({'status':'success'})

I want to verify if that view returns correct response using unit test:

class AddItemToCollectionTest(TestCase):

    def test_success_when_not_added_before(self):
        response = self.client.post('/add-item-to-collection')
        self.assertEqual(response.status_code, 200)
        self.assertJSONEqual(response.content, {'status': 'success'})

However the assertJSONEqual() line raises an exception:

Error
Traceback (most recent call last):
  File "E:\Projects\collecthub\app\collecthub\collecting\tests.py", line 148, in test_success_when_added_before
    self.assertJSONEqual(response.content, {'status': 'OK'})
  File "E:\Projects\collecthub\venv\lib\site-packages\django\test\testcases.py", line 675, in assertJSONEqual
    data = json.loads(raw)
  File "C:\Python34\Lib\json\__init__.py", line 312, in loads
    s.__class__.__name__))
TypeError: the JSON object must be str, not 'bytes'

What is thet correct way of checking content of response, when response contains JSON? Why i get type error when i try to compare raw value agains a dict in assertJSONEqual() ?

Mariusz Jamro
  • 30,615
  • 24
  • 120
  • 162

2 Answers2

49

It looks like you're working with Python 3 so you'll need to turn response.content into a UTF-8 encoded string before passing it to self.assertJSONEqual:

class AddItemToCollectionTest(TestCase):

    def test_success_when_not_added_before(self):
        response = self.client.post('/add-item-to-collection')
        self.assertEqual(response.status_code, 200)
        self.assertJSONEqual(
            str(response.content, encoding='utf8'),
            {'status': 'success'}
        )

If you want to simultaneously support both Python 2.7 and Python 3, use the six compatibility library that django ships with:

from __future__ import unicode_literals
from django.utils import six

class AddItemToCollectionTest(TestCase):

    def test_success_when_not_added_before(self):
        response = self.client.post('/add-item-to-collection')
        self.assertEqual(response.status_code, 200)

        response_content = response.content
        if six.PY3:
            response_content = str(response_content, encoding='utf8')

        self.assertJSONEqual(
            response_content,
            {'status': 'success'}
        )
respondcreate
  • 1,780
  • 1
  • 20
  • 23
  • 9
    Instead of `str`, this works too: `response.content.decode("utf-8")` – jaywink Nov 28 '15 at 15:11
  • 1
    Feel as though using `response.content.decode("utf-8")` makes more sense, as the `str()` method doesn't return a string – DrBuck Jan 17 '20 at 21:21
11

Similarly to respondcreate's solution, you can also use Django's force_text (available since version 1.5), for a shorter cross-platform solution:

from __future__ import unicode_literals
from django.utils.encoding import force_text

class AddItemToCollectionTest(TestCase):

    def test_success_when_not_added_before(self):
        response = self.client.post('/add-item-to-collection')
        self.assertEqual(response.status_code, 200)

        self.assertJSONEqual(force_text(response.content), {'status': 'success'})
tutuDajuju
  • 10,307
  • 6
  • 65
  • 88