Problème de login avec Django et Pytest
problème de login dans pytest
Bonsoir tout le monde, excellente année à vous toutes et tous.
Je lutte depuis des heures avec des tests de mon application sous django, sous pytest, qui nécessitent qu'un utilisateur créé au moyen d'une fixture soit connecté.
Le mot de passe est bien hashé, l'utilisateur est créé mais rien n'y fait, impossible de faire fonctionner les tests qui nécessitent le login!! Déjà au niveau du client créé le login ne fonctionne pas, voici ma fixture pour créer l'utilisateur et celle pour le client où déjà ça bloque:
@pytest.fixture
def user(db):
return Patient.objects.create_user(
email="[email protected]",
password="oyeoye",
nom="Dupont",
prenom="Jean",
adresse="123 Rue Exemple",
genre="M",
)
J'ai effectué des tests pour vérifier le hachage et le statut is_active, ils sont ok.
@pytest.fixture
def client(user):
client = Client()
logged_in = client.login(email=user.email, password="oyeoye")
assert logged_in, "client not logged_in"
return client
Merci pour votre aide.
Ali
Salut :)
Déjà, pour commencer tu peux faire du force login (imaginons une fixture user_1). Si tu ne test pas le login en soit, c'est bien mieux de faire comme ça.
import pytest
from django.test import Client
from django.urls import reverse
def test_stories_view(client: Client, user_1):
client.force_login(user_1)
response = client.get(reverse("rpg:stories-list"))
Sinon pour ton cas précis essaye ça stp :
@pytest.fixture
def client(user):
client = Client()
logged_in = client.login(username=user.email, password="oyeoye")
assert logged_in, "client not logged_in"
return client
Hey Gabriel, merci pour ta réponse. Effectivement j'ai utilisé force_login pour pas mal de tests mais il subsiste un test de connexion malheureusement et donc j'ai besoin de ne pas "forcer" cette connexion.
J'avais également remplacé email par username car j'utilise un customuser basé sur email et password mais ça ne fonctionne pas, la connexion via le client échoue toujours. J'ai le bon backend d'authentification dans mes settings.
Voici le retour:
@pytest.fixture
def client(user):
client = Client()
logged_in = client.login(username=user.email, password="<password>")
> assert logged_in, "client not logged_in"
E AssertionError: client not logged_in
E assert False
soignemoiwebsite/tests/test_vues.py:40: AssertionError
========================================================================================================= short test summary info =========================================================================================================
ERROR soignemoiwebsite/tests/test_vues.py::test_sejour_view - AssertionError: client not logged_in
et le test de connexion justement qui échoue (j'ai enlevé la partie assert logged_in dans la fixture du Client). Est-il correct?
def test_login_user(client):
url = reverse('soignemoiwebsite:login')
response = client.post(url, {'username': '[email protected]', 'password': 'oyeoye'})
> assert response.status_code == 302, "login did not redirect as expected"
E AssertionError: login did not redirect as expected
E assert 200 == 302
E + where 200 = <httpresponse "text="" charset='utf-8"' html;="" status_code="200,">.status_code
soignemoiwebsite/tests/test_vues.py:133: AssertionError
========================================================================================================= short test summary info =========================================================================================================
FAILED soignemoiwebsite/tests/test_vues.py::test_login_user - AssertionError: login did not redirect as expected
Merci pour ton aide.
PS: j'ai pu lire que CSRF pouvait aider, ou bien ajouter transactional_db dans certains cas mais ça va au-delà de mes compétences actuelles...
non ça ne fonctionne toujours pas,
`def test_login_user(client, user):
url = reverse('soignemoiwebsite:login')
response = client.post(url, {'username': user.email, 'password': 'oyeoye'})
assert response.status_code == 302, "login did not redirect as expected"E AssertionError: login did not redirect as expected
E assert 200 == 302
E + where 200 = <httpresponse "text="" charset='utf-8"' html;="" status_code="200,">.status_code`</httpresponse>
En fait bien avant d'en arriver à ce test, dès la définition de la fixture Client on a le problème puisque la connexion n'est pas établie (assert logged_in, "client not logged_in)...
Bon Gabriel je crois avoir trouvé après d'innombrables tests: il y avait un problème de hashage du password dû au fait que dans mon modèle CustomUser, ma surcharge de la méthode save était mal implémentée : pour que tous mes mots de passe soient hashés quand ils sont définis en dehors de la méthode create_user (dans interface admin ou en appelant directement la classe Patient) j'ai implémenté un hashage mais de la mauvaise façon! Ceci provoquait un double hashage dans l'environnement de test et cela échouait.
# on surcharge save pour s'assurer du hachage du mot de passe en toute circonstance
def save(self, *args, **kwargs):
# Si le mot de passe n'est pas chiffré, on le chiffre.
if self.pk is None or not self.password.startswith('pbkdf2_'):
self.set_password(self.password) # la méthode set_password est liée à AbstractBaseUser donc appelable ici.
super().save(*args, **kwargs)
J'ai remplacé
or par and et miracle tout fonctionne
parce que je souhaitais que tous les mots de passe soient hashés, notamment quand je crée des utilisateurs depuis l'interface admin ou quand je crée un utilisateur en appelant sa classe ali = Patient(email=..., ...., password = 'password_en_clair)
Preneur dune meilleure facon de faire ;-)
Alors, imagine que l'on créé un CustomUser, je mets un exemple très simple et basique, avec son manager :
class CustomManager(BaseUserManager):
def create_user(self, username, email, password, **kwargs):
if not username:
raise ValueError("Username must be set")
if not email:
raise ValueError("Email must be set")
user = self.model(
username=username, email=self.normalize_email(email), **kwargs
)
user.set_password(password)
user.save()
return user
def create_superuser(self, username, email, password, **kwargs):
kwargs.setdefault("is_staff", True)
kwargs.setdefault("is_superuser", True)
if kwargs.get("is_staff") is not True:
raise ValueError("Superuser must have is_staff=True.")
if kwargs.get("is_superuser") is not True:
raise ValueError("Superuser must have is_superuser=True.")
return self.create_user(username, email, password, **kwargs)
class CustomUser(AbstractUser):
email = models.EmailField(unique=True)
REQUIRED_FIELDS = ["email"]
objects = CustomManager()
Là pas trop de customisation mais c'est pour l'example. C'est bien de se faire un manager custom en même temps que l'user custom, comme ça si tu rajoutes des champs dans le custom user, tu les gères dans ton manager.
Maintenant, pas besoin de toucher à save, car tu vas en réaliser utiliser create_user qui va s'occuper de hash le mot de passe :
user = CustomUser.objects.create_user(
username="john_doe",
email="[email protected]",
password="secret_pass"
)
Et pour l'administration je te laisse regarder j'en parle ici :
https://youtu.be/yLy8EfalCxQ
J'espère t'avoir aidé :)
A bientôt !
salut Gabriel, merci pour ton aide, la vidéo sur la gestion admin va beaucoup m'aider, d'autant que je ne crois pas me rappeler que l'on aborde UserAdmin dans la formation.
bonne année encore!
Inscris-toi
(c'est gratuit !)
Tu dois créer un compte pour participer aux discussions.
Créer un compte