Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test if a view is decorated with "login_required" (Django)

I'm doing some (isolated) unit test for a view which is decorated with "login_required". Example:

@login_required
def my_view(request):
    return HttpResponse('test')

Is it possible to test that the "my_view" function is decorated with "login_required"?

I know I can test the behaviour (anonymous user is redirected to login page) with an integration test (using the test client) but I'd like to do it with an isolated test.

Any idea?

Thanks!

like image 599
goathi Avatar asked Jun 19 '15 23:06

goathi


People also ask

How do I test a Django project?

The preferred way to write tests in Django is using the unittest module built-in to the Python standard library. This is covered in detail in the Writing and running tests document. You can also use any other Python test framework; Django provides an API and tools for that kind of integration.

What should you test in Django?

Models. As discussed above, we should test anything that is part of our design or that is defined by code that we have written, but not libraries/code that is already tested by Django or the Python development team. For example, consider the Author model below.


2 Answers

Sure, it must be possible to test it in some way. It's definitely not worth it, though. Writing a fully isolated unit test to check that the decorator is applied will only result in a very complicated test. There is a way higher chance that the test will be wrong than that the tested behaviour is wrong. I would strongly discourage it.

The easiest way to test it is to use Django's Client to fake a request to the associated url, and check for a redirect. If you're using any of Django's testcases as your base class:

class MyTestCase(django.test.TestCase):
    def test_login_required(self):
        response = self.client.get(reverse(my_view))
        self.assertRedirects(response, reverse('login'))

A slightly more complicated, but a bit more isolated test would be to call the view directly using the RequestFactory to create a request object. assertRedirects() won't work in this case, since it depends on attributes set by the Client:

from django.test.client import RequestFactory

class MyTestCase(django.test.TestCase):
    @classmethod
    def setUpClass(cls):
        super(MyTestCase, cls).setUpClass()
        self.rf = RequestFactory()

    def test_login_required(self):
        request = self.rf.get('/path/to/view')
        response = my_view(request, *args, **kwargs)
        self.assertEqual(response.status_code, 302)
        self.assertEqual(response['Location'], login_url)
        ...
like image 199
knbk Avatar answered Oct 01 '22 21:10

knbk


Use Django's Test Client to check for proper redirects in case a user is loggedin and when not loggedin.

from django.test import TestCase
from django.core.urlresolvers import reverse

class TestLoginRequired(django.test.TestCase):

    def test_redirects_to_login_page_on_not_loggedin(self):
        response = self.client.get(reverse(my_view))
        self.assertRedirects(response, reverse('login_page'))

    def test_redirects_to_test_page_on_loggedin(self):
        self.client.login(username='my_username', password='my_password')
        response = self.client.get(reverse(my_view))
        self.assertRedirects(response, reverse('test'))

MOCK Library:

For an isolated test or 'pure' unit testing, you can use the mock module.

Mock is a library for testing in Python. It allows you to replace parts of your system under test with mock objects and make assertions about how they have been used.
Mock is based on the ‘action -> assertion’ pattern instead of ‘record -> replay’ used by many mocking frameworks.

You will have to create a mock object. Mock objects create all attributes and methods as you access them and store details of how they have been used. You can configure them, to specify return values or limit what attributes are available, and then make assertions about how they have been used:

The tests with mock objects will test only whether my_view function is decorated with login_required. You don't need to setup other attributes.

Check out the docs on how to write tests using mock objects using this link.

Also, following SO links might help on how to monkey-patch a decorator.

  • How can I monkey-patch a decorator in Django's models while testing?
  • Can I patch a Python decorator before it wraps a function?
like image 44
Rahul Gupta Avatar answered Oct 01 '22 19:10

Rahul Gupta