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).
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())
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>')
The gotcha here is that the is_authenticated
method always returns True
for a User
instance.
In the template or view it can be useful - if request.user.is_authenticated()
is True
, then you have a real user, not an AnonymousUser
, and can proceed accordingly. However, if you start off with a User
object, as you have done, then is_authenticated()
can be confusing!
You can check the return value of the test client's login()
method to test whether it was successful.
login_successful = c.login(username='fred', password='secret')
self.assertTrue(login_successful)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With