Search code examples
djangounit-testingdjango-authenticationdjango-testing

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


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.


Solution

  • 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.