Résolue

Tests unitaires : login

# Django # Tests unitaires

Gabriel Trouvé

Mentor

Bonsoir,

ça fait 45 min que je bloque donc je préfère demander... lol

class IdeaTest(TestCase):

    def setUp(self):
        self.category = Category.objects.create(name="Informatique")

        self.thinker = Thinker.objects.create_user(username="gabgab",
                                                   phone="0344805112",
                                                   email="[email protected]",
                                                   country="France",
                                                   first_name="Gabriel",
                                                   last_name="Trouvé",
                                                   password="testtesttest",
                                                   is_active=True)
        self.thinker2 = Thinker.objects.create_user(username="jeanjean",
                                                    phone="0344805112",
                                                    email="[email protected]",
                                                    country="France",
                                                    first_name="Gabriel",
                                                    last_name="Trouvé",
                                                    password="testtesttest",
                                                    is_active=True)
        self.thinker3 = Thinker.objects.create_user(username="jj",
                                                    phone="0344805112",
                                                    email="[email protected]",
                                                    country="France",
                                                    first_name="Gabriel",
                                                    last_name="Trouvé",
                                                    password="12345678",
                                                    is_active=True)

        self.idea = Idea.objects.create(name="good idea", summary="test suumm", level="1",
                                        category=self.category, thinker=self.thinker,
                                        details="Bla bla bla")
        self.idea2 = Idea.objects.create(name="good idea2", summary="test suumm2", level="1",
                                         category=self.category, thinker=self.thinker,
                                         details="Bla bla bla2", paid=True, buyer=self.thinker2)


       def test_if_idea_bought_and_user_not_buyer_or_thinker(self):
            c = Client()
            c.login(email="[email protected]", password="12345678")
            resp = c.get(self.idea2.get_absolute_url())
            self.assertEqual(resp.status_code, 410)

Le test ne passe pas. En fait self.idea2 est en statut payé avec un thinker et un buyer du coup. Je connecte donc self.thinker3 qui n'est ni buyer ni thinker.

Le resultat du test est une erreur 302, comme si l'utilisateur n'était pas connecté.
Pourtant quand je test en direct dans mon projet j'ai bien une page erreur 410 qui s'affiche.

Qu'est-ce qui est mal dans mon test ? L'utilisateur ne se connecte pas ?
Car s'il l'était il serait redirigé vers une page erreur 410 :

@login_required()
def idea_detail_view(request, slug):
    user = request.user
    idea = get_object_or_404(Idea, slug=slug)
    if idea.paid:
        # Si l'idée est payée
        if user != idea.thinker and user != idea.buyer:
            # Si l'utilisateur est différent de l'auteur ou si l'utilisateur est différent de l'acheteur
            # Si je suis auteur : False and True = False
            # Si je suis acheteur : True and False = False
            # Si je ne suis ni l'un ni l'autre : True and True = True
            return HttpResponse(status=410)

    comments = Comment.objects.filter(idea=idea).order_by('date')

    if request.method == "POST":
        form = IdeaCommentForm(request.POST)
        if form.is_valid():
            form.instance.user = user
            form.instance.idea = idea
            form.save()
            return redirect(idea)
    else:
        form = IdeaCommentForm()

    return render(request, "ideas/idea.html", context={"idea": idea, "form": form,
                                                       "comments": comments})

Merci d'avance

PS : Je suis le seul à avoir du mal avec les tests unitaires ? (j'en fait pas assez je pense)

Thibault houdon

Mentor

Salut Gab et désolé pour le délai de réponse !

Pour ce qui est de ton test, je pense que l'erreur vient de la manière dont tu essaies de te connecter. Tu utilises la méthode login avec "email" et "password" comme arguments. Or, par défaut, la méthode attend "username" et "password" (voir la doc)

c = Client()
c.login(username="jj", password="12345678")

Normalement tu devrais également pouvoir passer directement l'utilisateur au paramètre user :

client.login(user=self.thinker3)

Et non, t'es pas le seul à avoir du mal avec les tests unitaires ne t'en fais pas. C'est un sujet complexe de base avec beaucoup de concepts à appréhender, beaucoup de choses qui peuvent mal fonctionner aussi en fonction de comment ta base de donnée est configuré et la machine sur laquelle tu lances les tests. Donc pas d'inquiétude, ça prends des mois voire des années à bien maîtriser ce sujet et aller au-delà des tests unitaires (et voir aussi les tests d'intégrations, etc).

Gabriel Trouvé

Mentor

Merci Thibault,

c'est dingue,

j'ai essayé avec username, avec le paramètre user directement rien n'y fait.

J'ai même mis un print pour vérifier s'il y a bien le login qui se fait ! lol
Mais j'ai essayé de 3 manières différentes l'utilisateur n'est jamais connecté.

    def test_if_idea_bought_and_user_not_buyer_or_thinker(self):
        c = Client()
        verif_log = c.login(username="jj", password="12345678")
        if verif_log:
            print("oui")
        else:
            print("non")
        resp = c.get(self.idea2.get_absolute_url())
        self.assertEqual(resp.status_code, 410)

Dans mon modèle utilisateur c'est avec l'email que je me log. J'ai pourtant essayé username, email, directement avec user.

USERNAME_FIELD = "email"

Thibault houdon

Mentor

Salut Gab ! Désolé pour le délai de réponse mais j'ai refais des tests et vérifié dans mes projets.

Je me souviens avoir eu le même problème avec le mot de passe et tu as une réponse sur ce fil StackOverflow :
https://stackoverflow.com/a/33294746

Si tu souhaites utiliser login, il faut donc passer par la méthode set_password pour que ça fonctionne et que le hash soit bien généré.

Et si tu souhaites "bypasser" l'authentification juste pour les tests, tu peux du coup utiliser force_login, je te laisse aller lire la doc pour comprendre les détails de cette méthode :
https://docs.djangoproject.com/fr/3.2/topics/testing/tools/#django.test.Client.force_login

Tiens-nous au courant !

Gabriel Trouvé

Mentor

Merci Thibault,

Mais il y a vraiment quelque chose que je ne dois pas piger... Je ne sais pas pourquoi j'ai voulu me forcer à faire des tests ! lol

Pour le coup je n'y arrive vraiment pas avec ce force_login.

   def test_if_idea_bought_and_user_not_buyer_or_thinker(self):
        c = Client()
        conect = c.force_login(self.thinker3)

        print(conect) # renvoie None

        resp = self.client.get(self.idea2.get_absolute_url())
        self.assertEqual(resp.status_code, 410)

Thibault houdon

Mentor

Salut Gab,

Effectivement, force_login ne renvoie rien, donc c'est tout à fait normal que ton 'print(conect)' renvoie None.

force_login va simplement simuler une authentification réussie pour l'utilisateur que tu as indiqué.

Au lieu de mettre self.idea2.get_absolute_url() tu as essayé de mettre l'URL directement en chaîne de caractères ? Ou de faire un debug pour voir si l'url retournée par get_absolute_url est bien la bonne. L'erreur pourrait venir de là aussi.

Aussi, plutôt que d'instancier un nouveau Client (avec c = Client()), tu devrais pouvoir utiliser self.client qui est créé automatiquement pour chaque test.
Donc, tu peux essayer quelque chose comme ceci :

   def test_if_idea_bought_and_user_not_buyer_or_thinker(self):
        self.client.force_login(self.thinker3)
        resp = self.client.get(self.idea2.get_absolute_url())
        self.assertEqual(resp.status_code, 410)

Et c'est vraiment bien que tu essaies de faire des tests, même si c'est un peu déroutant au début, ça finit par payer dans le long terme quand tu commences à avoir une base de code plus importante :) !

Gabriel Trouvé

Mentor

Ok j'ai fait ça :

    def test_if_idea_bought_and_user_not_buyer_or_thinker(self):
        self.client.force_login(self.thinker3)

        resp = self.client.get("/ideas/idea/demande-didee/")
        print(resp)
        self.assertEqual(resp.status_code, 410)

Le force_login ne s'exécute pas apparemment. Si cet utilisateur était connecté j'aurais une 410.

Pourtant dans la doc et sur stack ils montrent bien que le force_login est suffisant en soi. Mais j'ai vu qu'il le mettait dans la méthodes SetUp des fois.

Thibault houdon

Mentor

Salut Gab !

Tu as un code de réponse 302, il s'agit d'une redirection, je me demande si ce n'est pas un problème dû à ton URL et / ou au paramètre append-slash.

À quoi ressemble ton fichier d'URL et ta vue qui mène à ideas/idea/demande-didee/ ?

Gabriel Trouvé

Mentor

URL:

path('idea/<str:slug>/', idea_detail_view, name="idea-detail"),
@login_required()
def idea_detail_view(request, slug):
    user = request.user
    idea = get_object_or_404(Idea, slug=slug)
    if idea.paid:
        # Si l'idée est payée
        if user != idea.thinker and user != idea.buyer:
            # Si l'utilisateur est différent de l'auteur ou si l'utilisateur est différent de l'acheteur
            # Si je suis auteur : False and True = False
            # Si je suis acheteur : True and False = False
            # Si je ne suis ni l'un ni l'autre : True and True = True
            return HttpResponse(status=410)

    comments = Comment.objects.filter(idea=idea).order_by('date')

    if request.method == "POST":
        form = IdeaCommentForm(request.POST)
        if form.is_valid():
            form.instance.user = user
            form.instance.idea = idea
            form.save()
            return redirect(idea)
    else:
        form = IdeaCommentForm()

    return render(request, "ideas/idea.html", context={"idea": idea, "form": form,
                                                       "comments": comments})

La redirection c'est voulu oui, c'est mon login_required().
L'utilisateur qui se connecte n'est ni acheteur ni auteur dans mon test. Du coup je veux atteindre la 410.

ça fonctionne bien d'ailleurs dans mon projet.

Thibault houdon

Mentor

Salut Gab !

Un truc qui me vient en tête : vérifie que les utilisateurs que tu crées avec create_user sont bien actif. Dans create_user tu envoie bien is_active=True, mais si tu as modifié la fonction create_user, peut-être as-tu oubligé de passer ce paramètre ?

Du coup vérifie cela dans ton test (ou dans ta fonction create_user si tu l'as modifié). Parce que tu ne peux pas faire un force_login sur un utilisateur inactif.

Gabriel Trouvé

Mentor

Salut Thibault !

Oui j'ai bien is_active=True car dans mon app les utilisateurs doivent activer leur compte par mail.

        self.thinker3 = Thinker.objects.create_user(username="gabgab",
                                                    phone="0344805112",
                                                    email="[email protected]",
                                                    country="France",
                                                    first_name="Gabriel",
                                                    last_name="Trouvé",
                                                    password="12345678",
                                                    is_active=True)

Et tu as vérifié après avoir créé ton utilisateur que le active est bien à True ? (assert self.thinker3.active is True) ? Tu n'as pas modifié la fonction create_user si je comprends bien ?

tadaaaaa !

Merci Thibault !

Si j'avais modif create_user avec is_active = False.

Du coup c'est ok !!

    def test_if_idea_bought_and_user_not_buyer_or_thinker(self):
        self.thinker3.is_active = True
        self.thinker3.save()
        self.client.force_login(self.thinker3)

        resp = self.client.get(self.idea2.get_absolute_url())
        print(resp)
        print(self.thinker3.is_active)
        self.assertEqual(resp.status_code, 410)

<httpresponse "text="" charset='utf-8"' html;="" status_code="410,">
</httpresponse>

Inscris-toi

(c'est gratuit !)

Inscris-toi

Tu dois créer un compte pour participer aux discussions.

Créer un compte

Rechercher sur le site

Formulaire de contact

Inscris-toi à Docstring

Pour commencer ton apprentissage.

Tu as déjà un compte ? Connecte-toi.