Djangos self.client.login (…) funktioniert nicht in Unit-Tests

83

Ich habe Benutzer für meine Komponententests auf zwei Arten erstellt:

1) Erstellen Sie ein Fixture für "auth.user", das ungefähr so ​​aussieht:

    { 
        "pk": 1, 
        "model": "auth.user", 
        "fields": { 
            "username": "homer", 
            "is_active": 1, 
            "password": 
"sha1$72cd3$4935449e2cd7efb8b3723fb9958fe3bb100a30f2", 
            ... 
        } 
    }

Ich habe die scheinbar unwichtigen Teile weggelassen.

2) Verwenden Sie 'create_user' in der setUp-Funktion (obwohl ich lieber alles in meiner Fixtures-Klasse behalten möchte):

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

Beachten Sie, dass das Passwort in beiden Fällen simpson ist.

Ich habe überprüft, ob diese Informationen immer wieder korrekt in die Testdatenbank geladen werden. Ich kann das User-Objekt mit User.objects.get abrufen. Ich kann mit 'check_password' überprüfen, ob das Passwort korrekt ist. Der Benutzer ist aktiv.

Self.client.login (Benutzername = 'Homer', Passwort = 'Simpson') schlägt jedoch immer fehl. Ich bin verblüfft, warum. Ich glaube, ich habe jede einzelne diesbezügliche Internetdiskussion gelesen. Kann jemand helfen?

Der Login-Code in meinem Unit-Test sieht folgendermaßen aus:

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

Vielen Dank.

thebossman
quelle
1
Was ist die Fehlermeldung, die Sie erhalten haben?
zs2020
Der Testfall schlägt in der Zeile 'self.assertTrue (login)' fehl. Die Funktion login () gibt False zurück.
Thebossman
1
Ich habe im Grunde Ihre 2. Variante kopiert und eingefügt und sie funktioniert auf Django 1.3. Können Sie den gesamten Code einschließlich der Importe veröffentlichen?
Liorsion
Dies wurde irgendwo in einer Codebasis vergraben, auf die ich nicht mehr zugreifen kann. Wenn ich auf das Problem stoße, werde ich es sicher weiterverfolgen, aber für die Aufzeichnung war es mit einer früheren Version von Django; Ich denke 1.0.2.
Thebossman

Antworten:

119

Der Code, der nicht funktioniert:

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

Warum funktioniert es nicht?

Im obigen Snippet wird beim UserErstellen von der tatsächliche Kennwort-Hash auf gesetzt 12345. Wenn der Client die loginMethode aufruft , wird der Wert des passwordArguments 12345durch die Hash-Funktion übergeben, was zu so etwas wie führt

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

Dies wird dann mit dem in der Datenbank gespeicherten Hash verglichen, und dem Client wird der Zugriff verweigert, weil 'adkfh5lkad438....' != '12345'

Die Lösung

Am besten rufen Sie die set_passwordFunktion auf, die den angegebenen String durch die Hash-Funktion leitet und das Ergebnis in speichert User.password.

Außerdem set_passwordmüssen wir nach dem Aufruf das aktualisierte UserObjekt in der Datenbank speichern:

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

c = Client()
logged_in = c.login(username='testuser', password='12345')
Pedro M Duarte
quelle
44
Sie eine Verwendung User.objects.create_superuser()und User.objects.create_user()die genau diesen set_password()Anruf ausführen .
Vdboor
Wie wäre es, wenn Sie eine Person anmelden
Rocky Raccoon
um @vdboor Kommentar hinzuzufügen: Beachten Sie, dass Django 1.4 auch User.objects.get_or_create()den erforderlichen set_password()Aufruf ausführt
Furins
51

Eine einfachere Möglichkeit ist die Verwendung force_login, neu in Django 1.9.

force_login(user, backend=None)

Zum Beispiel:

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

Können Sie wie unten überprüfen,

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)

Dieser Testfall hat bei mir funktioniert.

Muthuvel
quelle
5

Überprüfen Sie, ob dies django.contrib.sessionshinzugefügt wurde, INSTALLED_APPSda client.login()überprüft wird, ob dies der Fall ist und immer false zurückgibt, wenn dies nicht der Fall ist:

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

e-satis
quelle
2
+1 dafür. Selbst django.contrib.sessions.middleware.SessionMiddlewarein Middleware-Klassen können Sie sich nicht über django.test.Client anmelden. Ich brauche eine Woche, um diese Antwort zu finden
Schüler
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)
aravind allu
quelle
-3

Wenn jemand dies noch verfolgt, sollten die Attribute 'is_staff' und 'is_active' für eine erfolgreiche Anmeldung auf True gesetzt bleiben.

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

Arindam Roychowdhury
quelle
4
Dies hat gerade funktioniert ........... self.user = User.objects.create (Benutzername = 'Testbenutzer', Passwort = '12345', is_active = True, is_staff = True, is_superuser = True) self. user.set_password ('hallo') self.user.save () user = authentifizieren (Benutzername = 'Testbenutzer', Passwort = 'Hallo') login = self.c.login (Benutzername = 'Testbenutzer', Passwort = 'Hallo' ) self.assertTrue (Login)
Arindam Roychowdhury
Dies hat nichts damit zu tun, is_staffwas definiert, ob der Zugriff auf das Admin-Panel im Allgemeinen gewährt wird. Wie Sie sehen können, haben die Daten in der Erstbuchung bereits is_active = 1und das ist auch die Standardeinstellung für User.objects.create().
jnns
1
@arindamroychowdhury - Wow, das hat tatsächlich funktioniert .. (Ihr Kommentar, das heißt)
Richard de Wit
1
Es könnte ein Versionsproblem sein, aber ich musste Ihren Authentifizierungsanruf auskommentieren. Davon abgesehen hat Ihr Kommentar wie ein Champion funktioniert! Bei weiteren Tests stellte ich fest, dass is_active, is_superuser und is_staff alle unnötig waren. Das, was es getan hat, war der Aufruf von set_password.
Delphus333