Tester un modèle utilisateur personnalisé avec Coverage et les outils de test de Django

Django fournit de nombreux outils de test; dans ce didacticiel, on utilisera certains d'entre eux pour tester une application avec un modèle d'utilisateur et une authentification personnalisés.

13 Juillet 2020 13:11
Thèmes: Modèle d'utilisateur personnalisé Authentification Tester

On va tester l'application userauth qu'on a créée dans un didacticiel précédent. Il propose un modèle d'utilisateur personnalisé et une authentification avec allauth. La commande pour exécuter des tests pour une application ou un répertoire spécifique est:

python3 manage.py test userauth

L'argument test est comparable à d'autres arguments utilisés par Django, tels que runserver, createsuperuser, migrate etc. Vous pourriez obtenir l'erreur suivante:

Got an error creating the test database: permission denied to create database

C'est parce que Django créera une base de données de tests, qui sera détruite lorsque tous les tests auront été exécutés. Vous devez donc donner à l'utilisateur de la base de données l'autorisation de créer cette base de données de test. Accédez à l'invite de commande psql:

psql postgres

et tapez la commande suivante:

ALTER USER usr_pet CREATEDB;

usr_pet est votre utilisateur de base de données. Sortez psql avec \q.

Une autre erreur pouvant survenir est:

ValueError: Missing staticfiles manifest entry for 'images/favicon-32x32.png'

ou quelque chose de similaire. Cela est dû à l'utilisation de ManifestStaticFilesStorage dans le paramètre STATICFILES_STORAGE dans nos paramètres. Django recommande de définir ce paramètre à sa valeur par défaut lors des tests, alors mettez une marque de commentaire devant la ligne où ce paramètre est défini.

Maintenant, si on exécute python3 manage.py test userauth, Django vous dira combien de tests ont été exécutés et affichera toutes les erreurs. Même sans test, on peut toujours exécuter la commande test; Django passera par l'installation et fera d'autres choses, donc retournera OK ou détectera les erreurs s'il en a trouvé.

Il existe de nombreuses façons de tester et de nombreux articles sur ses limites. Deux méthodes de test populaires sont les tests de couverture (par exemple avec Coverage) et l'automatisation du navigateur (par exemple avec Selenium). Ici, on se limitera aux tests de couverture, qui peuvent être facilement intégrés à Django. Installez le package:

pip3 install coverage

Ajoutez-le à requirements.txt et ajoutez coverage à INSTALLED_APPS. Exécutez-le avec:

coverage run --source=userauth manage.py test userauth

L'option source indique à coverage de mesurer seulement le code dans le répertoire userauth, et le userauth à la fin de la commande indique à Django d'exécuter uniquement les tests dans l'application userauth. La commande suivante

coverage html

crée un répertoire htmlcov dans le répertoire du projet avec un fichier index.html dedans. Faites un clic droit dessus et choisissez open in browser. Pour notre application userauth, on voie qu'une partie substantielle du code est déjà visitée, même sans tests. En effet, dans notre application, on a utilisé au maximum les classes et méthodes intégrées de Django et allauth. Créons quelques tests pour les morceaux de code qui n'étaient pas couverts, en commençant par un simple: la représentation du modèle utilisateur personnalisé. Récapitulant de notre tutoriel précédent sur notre modèle d'utilisateur client:

def __str__(self):
    return f"{self.username}: {self.first_name} {self.last_name}"

On utilisera la classe Testcase de Django. On crée d'abord un utilisateur dans notre configuration avec setUpTestData, puis on teste avec assertEqual si la représentation est ce qu'elle devrait être:

from .models import CustomUser
from django.test import TestCase

class TestCustomUser(TestCase):

    @classmethod
    def setUpTestData(cls):
       cls.user = CustomUser.objects.create(username="userJohnDoe", password="secretpassword", first_name="John", last_name="Doe")

    def test_string_representation_of_customuser(self):
        expected_representation_customuser = "userJohnDoe: John Doe"
        self.assertEqual(expected_representation_customuser, str(self.user))

Exécuter coverage à nouveau révélera qu'on a maintenant couvert ce morceau de code. Ceci est bien sûr un exemple très simple. Testons la méthode get_absolute_url de notre modèle. Encore une fois à partir de notre tutoriel précédent:

def get_absolute_url(self):
    return reverse('account_profile')

avec ce qui suit dans notre urls.py:

path('profile/', profile_view, name='account_profile'),

et dans notre views.py:

def profile_view(request):
    return render(request, 'account/profile.html')

Un appel à profile_view avec un utilisateur connecté doit donner une réponse valide. La documentation Django décrivent comment simuler une connexion utilisateur à l'aide de RequestFactory, qui génère un objet de demande. On importe RequestFactory et profile_view:

from .views import profile_view
from django.test import RequestFactory

Dans notre setUpTestData, on ajoute la ligne:

cls.factory = RequestFactory()

puis notre test est (en utilisant l'attribut status_code de HttpResponse):

def test_profile_view_with_user_gets_valid_response(self):
    request = self.factory.get(self.user.get_absolute_url())
    # log user in
    request.user = self.user
    self.assertEqual(profile_view(request).status_code, 200)

Notre test final pour userauth sera sur LoginForm dans forms.py. Définissez un deuxième utilisateur dans setUpTestData:

cls.user2 = CustomUser.objects.create(username="undefined", password="undefined", first_name="undefined", last_name="undefined")

On ne peut pas réutiliser le premier utilisateur, car les tests ne sont pas nécessairement exécutés dans l'ordre dans lequel ils sont définis, ce qui signifie que les changements dans un test pourraient affecter les variables réutilisées dans un autre. Définissez des données de formulaire, alimentez-les vers SignupForm et utilisez la méthode signup sur l'utilisateur nouvellement créé pour vérifier si la méthode fait ce qu'elle est censée faire:

def test_signup_form(self):
    form_data = {'first_name': "Jane", 'last_name': "Doe", 'display_name': "Jane Doe"}
    form = SignupForm(data=form_data)
    self.assertTrue(form.is_valid())
    form.signup(self, user=self.user2)
    self.assertEqual(self.user2.display_name, "Jane Doe")

L'exécution répétée de coverage montre qu'on a couvert plus de 95% du code de userauth et que seules quelques lignes de code simples sont manquantes. On quitte les tests de cette application et continue.

Commentez cet article (connectez-vous d'abord ou confirmez par nom et email ci-dessous)