Django - Testing - Problems with @login_required decorator
django, django-authentication, python, testing, unit-testing
Solution
`user.password` is password hash. You can't log in with it. You need to use original password:
self.client.login(username=user.username, password='<user password>')
Problem
Problem UPDATE: This issue, it turns out, has little to do with the `@login_required` decorator! I am getting finicky behavior when I try to test views that are decorated with `@login_required`. I have one test that is actually able to go to a view decorated with `@login_required` (a password change view). A different test, however, always gets redirected to login. No matter which way I've tried to re-write it, it won't let my test user through, even though I am logging the user in and asserting that `user.is_authenticated()` beforehand. Here's the relevant snippet of the test that's having issues: ``` # Log user in self.client.login(username=user.username, password=user.password) self.assertTrue(user.is_authenticated()) # Go to account_edit view url = reverse('account_edit') response = self.client.get(url) self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, 'users/account_edit_form.html') ``` This inevitably redirects to the login view, as if the user were not logged in. I can confirm that this behavior is not reflected in the "normal" functioning of the app; the decorator works exactly as expected when I actually run the server and then navigate to this view (i.e. it allows access when logged in, and redirects to login view). Using Django 1.3.1. I am using the testrunner from django-nose, but I can confirm that this issue happens regardless of which testrunner I use. Also, I found several previous questions, but the solutions suggested were either particular to older versions of Django, or not helpful in this case (see here for example). Solution (combining two good answers) I received two very good answers to this question, both of which highlighted important oversights in the snippet I posted. The issue had nothing to do with `@login_required`'s behavior, and everything to do with (a) signing the user in wrong and (b) checking the user authentication wrong. I was having a hard time picking which answer to accept, but after a little thought, I've decided to accept Konrad Hałas's answer, since it pinpoints the crucial oversight on my part, that was the root of unexpected behavior. Nonetheless, I would have figured this out much sooner if I had not been using the faulty test line `self.assertTrue(user.is_authenticated())`. So to emphasize that the solution was actually two parts, here are the two steps to fixing the problematic code: ``` # Log user in *NOTE: Password needs to be raw (from Konrad's answer) self.client.login(username=user.username, password="pass") self.assertTrue(user.is_authenticated()) # <-- still not correct ``` The assertion line is still faulty because a valid user always satisfies `user.is_authenticated()`. See Alasdair's info for an explanation of this gotcha. So step two of fixing this code is: ``` # Log user in *NOTE: Password needs to be raw (from Konrad's answer) login_successful = self.client.login(username=user.username, password="pass") self.assertTrue(login_successful) # Much better! (see Alasdair's answer) ``` Finally, should you need to test that your user was logged in without using `client.login` (i.e. testing a login form), this should work: ``` self.assertTrue(response.context['user'].is_authenticated()) ```