Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

django RequestFactory loses url kwargs

Tags:

python

django

I am trying to switch from using Django Test Client to RequestFactory to speed up my tests. However, requests generated by RequestFactory do not supply proper kwargs to views.

Example: this is my view

class SomeView(View):
    def get(self, request, *args, **kwargs):
        return JsonResponse({'your kwargs': str(kwargs)})

with urlconf

url(r'^some_view/(?P<some_kwarg>[\-0-9a-fA-F]+)/$',
    views.SomeView.as_view(),
    name='some_view'),

and two tests:

def test_different_kwargs():

    c = Client()
    response = c.get(
        reverse('bots:some_view',
                kwargs={'some_kwarg': '12345'}),
    )
    print('\n\nResponse for TestClient: ', response.content.decode())

    rf = RequestFactory()
    request = rf.get(
        reverse('bots:some_view',
                kwargs={'some_kwarg': '12345'}),
    )
    response = SomeView.as_view()(request)
    print('\n\nResponse for RequestFactory: ', response.content.decode())

What they produce is:

Response for TestClient:  {"your kwargs": "{'some_kwarg': '12345'}"}
Response for RequestFactory:  {"your kwargs": "{}"}

So, what's the point of RequestFactory if it loses url kwargs? Or is there a way to put them into the view somehow?

like image 629
kurtgn Avatar asked Feb 02 '18 10:02

kurtgn


1 Answers

EDIT 2020-03-07: As gain I more experience in testing, I updated my answer to remove confusion around functional testing and I added some advises.

There are two aspects to your answer.

Quick answer: how to put the kwargs in the view? You need to change your code to this:

def test_different_kwargs():
    kwargs={'some_kwarg': '12345'}
    url = reverse('bots:some_view', kwargs=kwargs)
    c = Client()
    response = c.get(url)
    print('\n\nResponse for TestClient: ', response.content.decode())

    rf = RequestFactory()
    request = rf.get(url)
    response = SomeView.as_view()(request, **kwargs)
    print('\n\nResponse for RequestFactory: ', response.content.decode())

Long answer: Then the difference between RequestFactory and Client: It has been developed a bit here: Django test RequestFactory vs Client but I would like to complete it a bit.

In terms of functionality Client handle the whole stack used to process the response including middlewares and url resolutions (url matching and parameters extraction). On the other side RequestFactory, just build a request object, leaving the responsibility to the user to add the proper attributes and to call the appropriate view functions or view methods.

Hence the call to ClassView.as_view()(request, *args, **kwargs) in the second case.

In terms of testing, Client is focused on integration testing (you will test that all the different parts fit together: middlewares, class-based/function view, template, templatetags), it's the end-to-end mechanism that you are testing here.

Client -> { UrlResolver -> Middleware -> View -> Middlewares -> TemplateResponse } -> Tests

Using RequestFactory you can focus on testing smaller parts, a class-based view method, a function view, a middleware method etc. In that sense RequestFactory is more related to unit testing.

See Request Factory Reference

If you are interest in unit tests and mocking methods, there is not much literature on this but you can look at this article (Testing Django Views in Isolation)[https://matthewdaly.co.uk/blog/2015/08/02/testing-django-views-in-isolation/].

In the end it all depends on how much you time can focus on testing. Integration/Functional/Unit testings have different advantages and drawbacks.

Advises (may be biased): If you develop a website, I advise the following:

  • focus on integration testing by testing routes and their expected behaviors ;
  • add unit/integration tests for your business logic (models in Django) ;

Unit testing parts using RequestFactory will take you more time and won't bring much benefits over using the Client API. Using Client API you will be closer to how your website will be used and how it will behave.

like image 138
Gabriel Pichot Avatar answered Nov 08 '22 11:11

Gabriel Pichot