103

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.

2) 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', 'ho...@simpson.net', '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.

thebossman
  • 4,598
  • 11
  • 34
  • 45
  • 1
    What is the error message you got? – zs2020 Apr 11 '10 at 23:21
  • The test case fails on the line, 'self.assertTrue(login)'; the login() function returns False. – thebossman Apr 12 '10 at 00:27
  • 1
    I basically copied&pasted your 2nd variation and it works on Django 1.3. Can you post the entire code including the imports? – Liorsion Jan 25 '11 at 16:25
  • This has been buried somewhere in a code base I no longer access. If I come across the problem I'll be sure to follow-up, but for the record, it was with an earlier version of Django; I think 1.0.2. – thebossman Jan 27 '11 at 09:32

8 Answers8

170

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')

OR, using more specialized APIs:

# this encodes the password and calls save()
user = User.objects.create_user(username='testuser', password='12345')

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

If you want a super user, use create_superuser.

dfrankow
  • 20,191
  • 41
  • 152
  • 214
Pedro M Duarte
  • 26,823
  • 7
  • 44
  • 43
76

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])
powlo
  • 2,538
  • 3
  • 28
  • 38
WeizhongTu
  • 6,124
  • 4
  • 37
  • 51
6

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

Chris Adams
  • 4,966
  • 1
  • 30
  • 28
Bite code
  • 578,959
  • 113
  • 301
  • 329
  • 2
    +1 for this. Even with `django.contrib.sessions.middleware.SessionMiddleware` in middleware classses you still can not login via django.test.Client. It takes me a week to find this answer – pupil Apr 07 '16 at 05:29
5

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='admin@admin.com')
    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.

Muthuvel
  • 506
  • 2
  • 6
  • 16
1

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.

kenny_knp
  • 49
  • 1
  • 7
0
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)
0

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)

Ehsan Ahmadi
  • 1,382
  • 15
  • 16
-4

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)

Arindam Roychowdhury
  • 5,927
  • 5
  • 55
  • 63
  • 4
    This just worked........... self.user = User.objects.create(username='testuser', password='12345', is_active=True, is_staff=True, is_superuser=True) self.user.set_password('hello') self.user.save() user = authenticate(username='testuser', password='hello') login = self.c.login(username='testuser', password='hello') self.assertTrue(login) – Arindam Roychowdhury Nov 14 '12 at 12:08
  • This has nothing to do with `is_staff` which defines wheter access to the admin panel in general is granted. As you can see the data in the initial posting already has `is_active = 1` and that's also the default for `User.objects.create()`. – jnns Nov 14 '13 at 09:46
  • 1
    @arindamroychowdhury - Wow, that actually worked.. (Your comment, that is) – Richard de Wit Jun 19 '14 at 07:20
  • 1
    It might be a version issue, but I had to comment out your authenticate call. Other than that, your comment worked like a champ! On further testing thought, I found that is_active, is_superuser, and is_staff were all unnecessary. The thing that did it was the call to set_password. – dolphus333 Apr 29 '16 at 13:51