Django's self.client.login(...) does not work in unit tests

DjangoUnit TestingDjango AuthenticationDjango Testing

Django Problem Overview


I have created users for my unit tests in two ways:

  1. Create a fixture for "auth.user" that looks roughly like this:

     { 
         "pk": 1, 
         "model": "auth.user", 
         "fields": { 
             "username": "homer", 
             "is_active": 1, 
             "password": 
    

    "sha1$72cd3$4935449e2cd7efb8b3723fb9958fe3bb100a30f2", ... } }

I've left out the seemingly unimportant parts.

  1. Use 'create_user' in the setUp function (although I'd rather keep everything in my fixtures class):

    def setUp(self): User.objects.create_user('homer', '[email protected]', 'simpson')

Note that the password is simpson in both cases.

I've verified that this info is correctly being loaded into the test database time and time again. I can grab the User object using User.objects.get. I can verify the password is correct using 'check_password.' The user is active.

Yet, invariably, self.client.login(username='homer', password='simpson') FAILS. I'm baffled as to why. I think I've read every single Internet discussion pertaining to this. Can anybody help?

The login code in my unit test looks like this:

    login = self.client.login(username='homer', password='simpson') 
    self.assertTrue(login) 

Thanks.

Django Solutions


Solution 1 - Django

The code that doesn't work:

from django.contrib.auth.models import User
from django.test import Client

user = User.objects.create(username='testuser', password='12345')

c = Client()
logged_in = c.login(username='testuser', password='12345')

Why doesn't it work?
In the snippet above, when the User is created the actual password hash is set to be 12345. When the client calls the login method, the value of the password argument, 12345, is passed through the hash function, resulting in something like

hash('12345') = 'adkfh5lkad438....'

This is then compared to the hash stored in the database, and the client is denied access because 'adkfh5lkad438....' != '12345'

The Solution

The proper thing to do is call the set_password function, which passes the given string through the hash function and stores the result in User.password.

In addition, after calling set_password we must save the updated User object to the database:

user = User.objects.create(username='testuser')
user.set_password('12345')
user.save()

c = Client()
logged_in = c.login(username='testuser', password='12345')

Solution 2 - Django

An easier way is to use force_login, new in Django 1.9.

force_login(user, backend=None)

For example:

class LoginView(TestCase):
    def setUp(self):
        self.client.force_login(User.objects.get_or_create(username='testuser')[0])

Solution 3 - Django

Can you check like below,

from django.test import TransactionTestCase, Client

class UserHistoryTest(TransactionTestCase):
    self.user = User.objects.create(username='admin', password='pass@123', email='[email protected]')
    self.client = Client() # May be you have missed this line

    def test_history(self):
        self.client.login(username=self.user.username, password='pass@123')
        # get_history function having login_required decorator
        response = self.client.post(reverse('get_history'), {'user_id': self.user.id})
        self.assertEqual(response.status_code, 200)

This test case worked for me.

Solution 4 - Django

Check that django.contrib.sessions is added to INSTALLED_APPS because client.login() checks that it is and will always return false if it is not:

https://docs.djangoproject.com/es/1.9/topics/http/sessions/#enabling-sessions

Solution 5 - Django

from django.test import TestCase
from django.contrib.auth.models import User
from django.test import Client
class MyProfile(TestCase):
    @classmethod
    def setUpClass(self):
        self.username = 'dummy' + data + '@gmail.com'
        self.password = 'Dummy@123'
        user = User.objects.create(username=self.username)
        user.set_password(self.password)
        user.save()
        c = Client()
        self.client_object = c.login(username=self.username, password=self.password)
        self.content_type = "application/json"
        response = self.client_object.post('/api/my-profile/', content_type=self.content_type)

Solution 6 - Django

If you are using rest_framework, make sure session-based authentication is enabled. That was my issue.

Go to your settings.py file and check that REST_FRAMEWORK -> DEFAULT_AUTHENTICATION_CLASSES includes SessionAuthentication:

REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": [
        "rest_framework.authentication.TokenAuthentication",
        "rest_framework.authentication.SessionAuthentication",
    ],
    ...
}

It looks like the login method uses the vanilla Django session-based approach, so if you were only using rest_framework's token auth that's going to fail.

Solution 7 - Django

If you just need to have an authenticated user during testing the test cases you can use force_login which does not need any authentication properties just pass the user object.

    def test_something_view(self):
        client = Client()
        client.force_login(self.user)
        response = client.post(reverse('your custom url'), follow=True)
        self.assertEqual(response.status_code, 200)

Solution 8 - Django

If anyone still following this , I think the attributes 'is_staff' and 'is_active' should be kept True for successfully logging in......

self.user = User.objects.create(username='testuser',password='pwd',is_active=1,is_staff=1)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionthebossmanView Question on Stackoverflow
Solution 1 - DjangoPedro M DuarteView Answer on Stackoverflow
Solution 2 - DjangoWeizhongTuView Answer on Stackoverflow
Solution 3 - DjangoMuthuvelView Answer on Stackoverflow
Solution 4 - Djangoe-satisView Answer on Stackoverflow
Solution 5 - Djangoaravind alluView Answer on Stackoverflow
Solution 6 - Djangokenny_knpView Answer on Stackoverflow
Solution 7 - DjangoEhsan AhmadiView Answer on Stackoverflow
Solution 8 - DjangoArindam RoychowdhuryView Answer on Stackoverflow