1. Livres & vidéos
  2. Django
  3. Tests
Extrait - Django Concevez vos applications web professionnelles en Python : du modèle de données au déploiement
Extraits du livre
Django Concevez vos applications web professionnelles en Python : du modèle de données au déploiement Revenir à la page d'achat du livre

Tests

Usines

Pour visualiser le code relatif à ce chapitre : git diff v1.13.2..v1.14.0.

1. Installer les deux modules indispensables

Pour commencer, nous allons installer deux outils : factory-boy et Faker.

Il faut soit démarrer une console, puis taper la commande :

$ make bash 
$ poetry add factory-boy faker  

Cette commande met à jour les fichiers pyproject.toml et poetry.lock.

Soit rajouter directement les lignes suivantes dans pyproject.toml :

factory-boy = "^3.3.3" 
faker = "^37.12.0"  

Puis, utiliser la commande suivante pour générer un fichier poetry.lock cohérent avec ce dernier :

$ make lock  

Une fois cette opération terminée, quelle que soit la méthode choisie, il faut reconstruire le conteneur :

$ make build  

Nous pouvons maintenant créer nos usines.

2. Créer une première usine

Nous allons créer les usines dans un fichier factories.py placé dans le répertoire de chaque application. Ce fichier regroupera les usines correspondant aux modèles propres à l’application.

Nous dérogerons toutefois immédiatement à cette règle en définissant également, dans le fichier app/factories.py, des usines destinées à générer des utilisateurs.

Voici cette usine :

from functools import partial 
from zoneinfo import ZoneInfo 
  
from django.contrib.auth import get_user_model 
  
import factory 
  
  
User = get_user_model() 
zone_info = ZoneInfo("Europe/Paris") 
  
Faker = partial(factory.Faker, locale="fr_FR") 
  
  
class UserFactory(factory.django.DjangoModelFactory): 
    class Meta: 
        model = User 
  
    username = factory.Sequence(lambda x: f"user_{x:02}") 
    first_name = Faker("first_name") 
    last_name = Faker("last_name") 
    email = factory.LazyAttribute(lambda x: 
f"{x.first_name}.{x.last_name}@example.com") 
    password = factory.PostGenerationMethodCall('set_password'...

Tests unitaires

Pour visualiser le code relatif à ce chapitre : git diff v1.12.0..v1.12.1.

1. Principes des tests unitaires

Les tests unitaires permettent de vérifier qu’un composant fonctionne comme prévu, aussi bien dans des conditions normales que face à des données invalides. Par exemple, si une chaîne de caractères est fournie à un champ qui attend un entier, une erreur explicite doit être obtenue.

Il faut donc tester les scénarios principaux, là où tout se passe bien et où on simule une utilisation correcte de nos composants, mais également les scénarios alternatifs, là où on simule une utilisation incorrecte de nos composants.

Il faut aussi avoir en tête les responsabilités de chaque composant et savoir vérifier que ces responsabilités sont assurées comme savoir ne pas demander à un composant quelque chose pour lequel il n’est pas prévu.

Pour créer un test unitaire, il suffit de définir une classe qui hérite de TestCase. Elle peut être placée dans le fichier tests.py de l’application. Dans notre cas, le nombre de tests justifie toutefois la création d’un package tests, contenant des modules nommés test_*.py. La découverte automatique s’appuie sur ces conventions de nommage.

Dans notre classe, toute fonction dont le nom commence par test_ sera considérée comme un test et sera exécutée en tant que tel.

Un autre principe important concerne le cycle de vie des tests. Par défaut, les méthodes de test sont chargées selon l’ordre alphabétique de leurs noms. Des méthodes de préparation et de nettoyage peuvent être exécutées automatiquement avant ou après la classe et avant ou après chaque test.

Imaginons que nous ayons une classe contenant deux tests, voici la séquence des méthodes de cette classe qui seront appelées :

  • setUpClass ;

  • setUp ;

  • test1 ;

  • tearDown ;

  • setUp ;

  • test2 ;

  • tearDown ;

  • tearDownClass.

Ainsi, pour préparer un jeu de données particulier ou supprimer un fichier créé pendant les tests, nous pouvons surcharger l’une des quatre méthodes suivantes : setUpClass, setUp, tearDown...

Test end to end

Pour visualiser le code relatif à ce chapitre : git diff v1.12.1..v1.12.2.

1. Installer Behave

Pour réaliser nos tests de bout en bout, nous souhaitons décrire les scénarios sous la forme de phrases compréhensibles par des Product Owner ou la MOA, sans exiger de connaissances techniques.

Nous allons donc utiliser Behave (https://behave-django.readthedocs.io/en/stable/).

Et parce qu’en cas d’erreur, nous avons besoin de ne pas perdre de temps à l’analyser, mais de comprendre de suite où elle se situe, nous allons utiliser PyHamCrest (https://pyhamcrest.readthedocs.io/en/latest/).

Enfin, les tests de bout en bout peuvent nécessiter un navigateur pour contrôler le rendu d’une page. Pour analyser le code HTML obtenu, nous utiliserons Beautiful Soup (https://www.crummy.com/software/BeautifulSoup/bs4/doc/).

Pour installer ces modules, il faut démarrer une console, puis taper la commande :

$ make bash 
$ poetry add behave-django pyhamcrest beautifulsoup  

Cette commande met à jour les fichiers pyproject.toml et poetry.lock.

Une autre option consiste à rajouter directement les lignes suivantes dans pyproject.toml :

behave-django = "^1.7.0" 
pyhamcrest = "^2.1.0" 
beautifulsoup4 = "^4.14.2"  

Puis, à utiliser la commande suivante pour générer un fichier poetry.lock cohérent :

$ make lock  

Une fois cette opération terminée, il faut reconstruire le conteneur :

$ make build  

Nous pouvons maintenant rajouter cette ligne dans la configuration de Django :

INSTALLED_APPS = [ 
    ... 
    'behave_django', 
    ... 
]  

Et créer cette nouvelle commande dans project/Makefile :

.PHONY: test_e2e 
test_e2e: 
       poetry run python manage.py behave  

Et celles-ci dans le Makefile de la racine :

.PHONY: test_e2e 
test_e2e: 
       docker compose run tuto_django make test_e2e 
  
.PHONY: test_all 
test_all: 
       make test_unit 
       make test_e2e  

Cette dernière va utiliser deux conteneurs, mais comme il est possible de paralléliser les...