Search code examples
djangodjango-testinglogin-required

Django test: how to compare result of resolve() with view function when login_required is specified for that view


In urls.py i have my view detail annotated with login_required to forward unauthorised users to login page:

url(r'^(?P<id>[0-9]+)/$', login_required(views.detail), name = 'detail')

And i'm trying to write a test to check which view are selected on querying target url. I've a class to log in before tests start off:

class LoggedInTestCase(TestCase):
    def setUp(self):
        user = User.objects.create_user('test', '[email protected]', 'test')
        self.client.login(username='test', password='test')

class ProductDetailTest(LoggedInTestCase):
    def setUp(self):
        super(ProductDetailTest, self).setUp()

    def test_product_detail_url_resolves_product_detail_view(self):
        view = resolve('/products/1/')
        self.assertEquals(view.func, detail)

and when i run tests i got:

FAIL: test_product_detail_url_resolves_product_detail_view (products.tests.ProductDetailTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "D:\heroku\django\products\tests.py", line 46, in test_product_detail_url_resolves_product_detail_view
    self.assertEquals(view.func, detail)
AssertionError: <function detail at 0x05CC3780> != <function detail at 0x053B38A0>

----------------------------------------------------------------------

To check which view are resolved

    print('VIEW: ', view.view_name)

and result is:

test_product_detail_url_resolves_product_detail_view (products.tests.ProductDetailTest) ... VIEW:  products:detail

When i remove login_required all tests pass well.


Solution

  • When you do login_required(detail), the decorator returns a new function. Your assertion should fail in the test, because the functions are not the same.

    Here's a couple of options. It might be simplest to use the decorator in the view:

    @login_required
    def detail(request, *args, **kwargs)
        ...
    

    and change the url pattern to:

    url(r'^(?P<id>[0-9]+)/$', views.detail, name = 'detail'),
    

    If you need to use the undecorated detail view, then you can't use the decorator like that. In that case, you assign login_required(detail) to a new variable,

    def detail(request, *args, **kwargs):
        ...
    
    login_required_detail = login_required(detail)
    

    change the URL pattern to,

    url(r'^(?P<id>[0-9]+)/$', views.login_required_detail, name = 'detail'),
    

    and change the test to :

    self.assertEquals(view.login_required_detail, detail)