joseglego
joseglego

Reputation: 2109

Django REST Framework - Set request in serializer test for ApiClient

I already read: Django REST Framework - Set request in serializer test?. And it doesn't work for me! Because I'm using APIClient and not RequestFactory like him.

I built a web app where the back-end is implemented using the Django REST Framework. Now I'm writing unit tests and I have come across a problem in testing my serializer methods. Here is one example of a serializer method I'm struggling with:

def get_can_edit(self, obj):
  request = self.context['request']
  user = User.objects.get(username=request.user)
  return user == obj.admin

When trying to call this from the test, first I declare an instance of the serializer:

But now I need self.serializer to have the correct request when get_can_edit does self.context.get('request'). I've created a fake request with the correct information using APIClient:

self.client = APIClient()
self.client.force_authenticate(user)
conference = a_fake_conference

res = self.client.get('conference:conference-detail'. args=[conference.id])
serializer = ConferenceSerializer(conference, context={WHAT_IS_REQUEST?}) 
# I'm using a dict as context but the request gave me an error: context={'request': { 'user': user }}

sert.assertEqual(res.data, serializer.data)

Now I am stuck because I am unsure how to add request1 to serializer such that request = self.context['request'] will return

Thanks.

Upvotes: 2

Views: 1690

Answers (2)

Daniel Quinn
Daniel Quinn

Reputation: 6408

You're testing two things with each other here, so that's sort of part of the problem. Typically, you'd use self.client.get(...) as an integration test, that is, a test that ensures that everything from request to response is working as expected. For tests like these, you wouldn't use a serialiser, because then you're using your application code (your serialiser) to test itself.

If you're writing an integration test, you should be testing the raw response with something like:

from django.test import TestCase, RequestFactory

class MyUnitTestCase(TestCase):
    def test_the_whole_stack(self):
        response = self.client.get(
            "conference:conference-detail", args=[conference.id]
        )
        self.assertEqual(response.status_code, 200)
        self.assertEqual(
            response.json()["some-key-you-expect"],
            "Some value you expect"
        )

Note that you also don't have to invoke APIClient() directly. It's there by default.

For a unit test, like the kind of test you'd write to make sure your serialiser is working properly, you don't need or want to be poking around with a WSGIRequest object. Instead, Django supplies a RequestFactory for just this case:

from django.test import TestCase, RequestFactory

class MyUnitTestCase(TestCase):
    def test_my_serialiser(self):
        serializer = ConferenceSerializer(
            conference,
            context={"request": RequestFactory().get("/")}
        )
        self.assertEqual(
            serialiser.data["some-key-you-expect"],
            "Some value you expect"
        )

Upvotes: 2

JPG
JPG

Reputation: 88499

Use wsgi_request (source code-Django) of APIClient to get the WSGI request object.

self.client = APIClient()
self.client.force_authenticate(user)
res = self.client.get('conference:conference-detail'. args=[conference.id]
# make sure to call the `get(...)` method before accessing `wsgi_request` attribute
request_object = res.wsgi_request

Disclaimer: Not sure whether this is a DRF way to get the request object.

Upvotes: 3

Related Questions