Thématiques de tests avancées¶
La fabrique de requêtes¶
La classe RequestFactory
partage la même API que le client de test. Cependant, au lieu de se comporter comme un navigateur, RequestFactory
offre une manière de générer une instance de requête pouvant être utilisée comme premier paramètre de n’importe quelle vue. Cela permet de tester une fonction de vue de la même façon que vous testeriez toute autre fonction, comme une boîte noire, avec des données connues en entrée et en testant un résultat spécifique.
L’API de RequestFactory
est un tout petit peu plus restreinte que celle du client de test :
- Elle ne donne accès qu’aux méthodes HTTP
get()
,post()
,put()
,delete()
,head()
,options()
ettrace()
. - Toutes ces méthodes acceptent les mêmes paramètres, à l’exception de
follow
. Comme il ne s’agit que d’une fabrique produisant des requêtes, le traitement de la réponse est de votre responsabilité. - Elle ne gère pas les intergiciels. Les attributs de session et d’authentification doivent être fournis par le test lui-même, si nécessaire, pour que la vue fonctionne correctement.
Exemple¶
L’exemple suivant est un test unitaire simple utilisant la fabrique de requêtes :
from django.contrib.auth.models import AnonymousUser, User
from django.test import RequestFactory, TestCase
from .views import MyView, my_view
class SimpleTest(TestCase):
def setUp(self):
# Every test needs access to the request factory.
self.factory = RequestFactory()
self.user = User.objects.create_user(
username='jacob', email='jacob@…', password='top_secret')
def test_details(self):
# Create an instance of a GET request.
request = self.factory.get('/customer/details')
# Recall that middleware are not supported. You can simulate a
# logged-in user by setting request.user manually.
request.user = self.user
# Or you can simulate an anonymous user by setting request.user to
# an AnonymousUser instance.
request.user = AnonymousUser()
# Test my_view() as if it were deployed at /customer/details
response = my_view(request)
# Use this syntax for class-based views.
response = MyView.as_view()(request)
self.assertEqual(response.status_code, 200)
Tests avec plusieurs noms d’hôtes¶
Le réglage ALLOWED_HOSTS
est validé lors de l’exécution des tests. Cela permet au client de test de faire la différence entre les URL internes et externes.
Les projets qui gèrent plusieurs domaines par site ou adaptent leur comportement en fonction de l’hôte de la requête et qui utilisent des noms d’hôte particuliers dans les tests doivent inclure ces noms d’hôte dans ALLOWED_HOSTS
.
L’option la plus évidente pour accomplir cela est d’ajouter les hôtes dans le fichier des réglages. Par exemple, la suite de tests de docs.djangoproject.com contient ceci :
from django.test import TestCase
class SearchFormTestCase(TestCase):
def test_empty_get(self):
response = self.client.get('/en/dev/search/', HTTP_HOST='docs.djangoproject.dev:8000')
self.assertEqual(response.status_code, 200)
et le fichier des réglages contient une liste des domaines pris en charge par le projet :
ALLOWED_HOSTS = [
'www.djangoproject.dev',
'docs.djangoproject.dev',
...
]
Une autre option est d’ajouter les hôtes nécessaires dans ALLOWED_HOSTS
en utilisant override_settings()
ou modify_settings()
. Cette option peut être préférable pour les applications autonomes qui ne peuvent pas fournir leur propre fichier de réglages ou pour les projets dont la liste des domaines n’est pas figée (par ex. des sous-domaines dans un service partagé). Par exemple, il serait possible d’écrire un test pour le domaine http://otherserver/
comme ceci :
from django.test import TestCase, override_settings
class MultiDomainTestCase(TestCase):
@override_settings(ALLOWED_HOSTS=['otherserver'])
def test_other_domain(self):
response = self.client.get('http://otherserver/foo/bar/')
La désactivation du contrôle de ALLOWED_HOSTS
(ALLOWED_HOSTS = ['*']
) lors de l’exécution des tests empêche le client de test de produire un message d’erreur instructif si une redirection est appelée pour une URL externe.
Tests avec plusieurs base de données¶
Test des configurations primaire/réplique¶
Si vous testez une configuration avec plusieurs bases de données impliquant une réplication primaire/réplique (désignée comme maître/esclave par certaines bases de données), cette stratégie de création de bases de données de test pose un problème. Lorsque les bases de données de test sont créées, la réplication n’a pas lieu et par conséquent, les données créées sur la base primaire ne seront pas visibles sur les répliques.
Pour contourner ce problème, Django permet de définir une base de données comme un miroir de test. Considérez cet exemple (simplifié) de configuration de bases de données :
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'myproject',
'HOST': 'dbprimary',
# ... plus some other settings
},
'replica': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'myproject',
'HOST': 'dbreplica',
'TEST': {
'MIRROR': 'default',
},
# ... plus some other settings
}
}
Dans cette configuration figurent deux serveurs de base de données : dbprimary
, défini par l’alias de base de données default
et dbreplica
, défini par l’alias replica
. Comme l’on peut s’y attendre, dbreplica
a été configuré par l’administrateur de base de données comme réplique en lecture de dbprimary
. En temps normal, toute écriture sur default
apparaît aussi sur replica
.
Si Django avait créé deux bases de données de test indépendantes, cela casserait d’éventuels tests s’attendant à de la réplication. Cependant, la base de données replica
a été configurée comme miroir de test (via le réglage de test MIRROR
), indiquant par là que lors des tests, replica
doit être traité comme un miroir de default
.
Au moment de la création de l’environnement de test, aucune base ne sera créée pour replica
. Par contre, la connexion vers replica
sera redirigée pour pointer vers default
. Par conséquent, les écritures sur default
apparaîtront aussi sur replica
, mais parce qu’il s’agit en réalité de la même base de données, car aucune réplication de données ne sera mise en place entre les deux bases de données.
Ordre de création des bases de données de test¶
Par défaut, Django part du principe que toutes les bases de données dépendent de la base de données default
et va par conséquent toujours créer celle-ci en premier. Cependant, pour toute autre base de données de votre configuration de test, il n’y a aucune garantie sur l’ordre de création des bases de données.
Si votre configuration de base de données nécessite un ordre de création bien défini, vous pouvez indiquer les dépendances entre bases de données dans le réglage de test DEPENDENCIES
. Considérez cet exemple (simplifié) de configuration de bases de données :
DATABASES = {
'default': {
# ... db settings
'TEST': {
'DEPENDENCIES': ['diamonds'],
},
},
'diamonds': {
# ... db settings
'TEST': {
'DEPENDENCIES': [],
},
},
'clubs': {
# ... db settings
'TEST': {
'DEPENDENCIES': ['diamonds'],
},
},
'spades': {
# ... db settings
'TEST': {
'DEPENDENCIES': ['diamonds', 'hearts'],
},
},
'hearts': {
# ... db settings
'TEST': {
'DEPENDENCIES': ['diamonds', 'clubs'],
},
}
}
Avec cette configuration, la base de données diamonds
sera créée en premier car c’est le seul alias de base de données sans dépendances. Les alias default
et clubs
seront ensuite créés (bien que l’ordre de création de ceux-ci ne soit pas défini), puis hearts
et enfin, spades
.
Si la définition de DEPENDENCIES
comporte des dépendances circulaires, une exception ImproperlyConfigured
sera générée.
Fonctionnalités avancées de TransactionTestCase
¶
-
TransactionTestCase.
available_apps
¶ Avertissement
Cet attribut est une API privée. Il peut être modifié ou supprimé dans une version future sans période d’obsolescence, par exemple pour s’adapter à des modifications du processus de chargement des applications.
Il est utilisé pour optimiser la suite de tests de Django lui-même qui contient des centaines de modèles mais sans relations entre les modèles des différentes applications de test.
Par défaut,
available_apps
vautNone
. Après chaque test, Django appelleflush
pour réinitialiser l’état de la base de données. Toutes les tables sont ainsi vidées et le signalpost_migrate
est émis, ce qui recrée un type de contenu et quatre permissions pour chaque modèle. Cette opération s’alourdit proportionnellement au nombre de modèles.En définissant
available_apps
à une liste d’applications, Django se comporte comme si seuls les modèles de ces applications étaient disponibles. Le comportement deTransactionTestCase
est modifié comme suit :post_migrate
est émis avant chaque test pour créer les types de contenu et les permissions de chaque modèle des applications disponibles, dans le cas où ils manqueraient.- Après chaque test, Django ne vide que les tables correspondants aux modèles des applications disponibles. Cependant, au niveau de la base de données, il est possible que les troncatures s’appliquent en cascade à des modèles liés dans des applications non disponibles. De plus,
post_migrate
n’est pas émis ; il sera émis par le prochain testTransactionTestCase
, après que l’ensemble d’applications adéquat a été sélectionné.
Comme la base de données n’est pas complètement vidée, si un test crée des instances de modèles non inclus dans
available_apps
, ces instances vont rester et pourraient être la cause d’échecs de tests non liés. Restez prudent avec les tests employant des sessions ; le moteur de sessions par défaut les stocke dans la base de données.Comme
post_migrate
n’est pas émis après la réinitialisation de la base de données, son état après unTransactionTestCase
n’est pas le même qu’après unTestCase
: les lignes créées par les récepteurs depost_migrate
sont manquantes. Mais sachant l’ordre dans lequel les tests sont exécutés, ce n’est pas un problème, dans la mesure où soit tous les testsTransactionTestCase
d’une suite de tests déclarentavailable_apps
, soit aucun.available_apps
est obligatoire dans la propre suite de tests de Django.
-
TransactionTestCase.
reset_sequences
¶ En définissant
reset_sequences = True
d’une classeTransactionTestCase
, vous vous assurez que les séquences sont toujours réinitialisées avant le lancement du test :class TestsThatDependsOnPrimaryKeySequences(TransactionTestCase): reset_sequences = True def test_animal_pk(self): lion = Animal.objects.create(name="lion", sound="roar") # lion.pk is guaranteed to always be 1 self.assertEqual(lion.pk, 1)
À l’exception des cas où vous testez explicitement les numéros de séquence de clés primaires, il est recommandé de ne pas tester des valeurs de clés primaires figées.
L’utilisation de
reset_sequences = True
ralentit les tests, car la réinitialisation des clés primaires est une opération de base de données relativement coûteuse.
Utilisation du lanceur de tests de Django pour tester des applications réutilisables¶
Si vous écrivez une application réutilisable, il peut être intéressant d’utiliser le lanceur de tests de Django pour lancer votre propre suite de tests et bénéficier ainsi de l’infrastructure de tests de Django.
Une pratique courante est de placer un répertoire tests en parallèle de votre code d’application, avec la structure suivante :
runtests.py
polls/
__init__.py
models.py
...
tests/
__init__.py
models.py
test_settings.py
tests.py
Examinons ce qui se trouve dans certains de ces fichiers :
#!/usr/bin/env python
import os
import sys
import django
from django.conf import settings
from django.test.utils import get_runner
if __name__ == "__main__":
os.environ['DJANGO_SETTINGS_MODULE'] = 'tests.test_settings'
django.setup()
TestRunner = get_runner(settings)
test_runner = TestRunner()
failures = test_runner.run_tests(["tests"])
sys.exit(bool(failures))
Il s’agit ici du script invoqué pour lancer la suite de tests. Il met en place l’environnement Django, crée la base de données de tests et lance les tests.
Par souci de clarté, cet exemple ne contient que le strict minimum nécessaire à l’utilisation du lanceur de tests de Django. On peut cependant imaginer ajouter des options en ligne de commande pour le contrôle de la verbosité, pour passer des étiquettes de tests spécifiques à lancer, etc.
SECRET_KEY = 'fake-key'
INSTALLED_APPS = [
"tests",
]
Ce fichier contient les réglages Django indispensables pour lancer les tests de votre application.
Encore une fois, il s’agit d’un exemple minimal ; les tests peuvent exiger des réglages supplémentaires pour fonctionner.
Comme le paquet tests est inclus dans INSTALLED_APPS
au moment de lancer les tests, il est possible de définir des modèles uniquement pour les tests dans le fichier models.py
.
Utilisation d’autres infrastructures de test¶
Il est clair que unittest
n’est pas la seule infrastructure de test de Python. Même si Django ne fournit pas de prise en charge explicite d’autres systèmes de test, il offre une manière d’invoquer des tests construits pour un autre système comme s’il s’agissait de tests Django normaux.
Lorsque vous lancez ./manage.py test
, Django consulte le réglage TEST_RUNNER
pour déterminer ce qu’il doit faire. Par défaut, TEST_RUNNER
contient 'django.test.runner.DiscoverRunner'
. Cette classe définit le comportement de test par défaut de Django. Ce comportement implique :
- Une préparation générale avant les tests.
- La recherche de tests dans tout fichier au-dessous du répertoire actuel dont le nom correspond au motif
test*.py
. - La création des bases de données de test.
- Le lancement de
migrate
pour installer les modèles et les données initiales dans les bases de données de test. - L’exécution des vérifications systèmes.
- L’exécution des tests découverts.
- La suppression des bases de données de test.
- Une finalisation générale après les tests.
Si vous définissez votre propre classe de lanceur de tests et que vous indiquez cette classe dans TEST_RUNNER
, Django se servira de votre lanceur de tests lors de chaque appel à ./manage.py test
. De cette façon, il est possible d’utiliser n’importe quel système de test pouvant être lancé à partir de code Python ou de modifier le processus d’exécution des tests Django pour adapter les tests à d’éventuels besoins spécifiques.
Définition d’un lanceur de tests¶
Un lanceur de tests est une classe définissant une méthode run_tests()
. Django fournit une classe DiscoverRunner
définissant le comportement de test par défaut de Django. Cette classe définit le point d’entrée run_tests()
ainsi qu’une sélection d’autres méthodes utilisées par run_tests()
pour préparer, exécuter et clore la suite de tests.
-
class
DiscoverRunner
(pattern='test*.py', top_level=None, verbosity=1, interactive=True, failfast=False, keepdb=False, reverse=False, debug_mode=False, debug_sql=False, **kwargs)[source]¶ DiscoverRunner
recherche des tests dans tout fichier correspondant au motifpattern
.top_level
peut être utilisé pour indiquer le répertoire contenant les modules Python de premier niveau. Django peut généralement le découvrir automatiquement, ce qui fait que cette option n’est pas absolument nécessaire. Lorsqu’elle est définie, elle devrait en principe correspondre au répertoire contenant le fichiermanage.py
.verbosity
détermine la quantité de notifications et d’informations de débogage qui s’afficheront sur la console ;0
n’affiche rien,1
correspond à l’affichage normal et2
à l’affichage verbeux.Si
interactive
vautTrue
, la suite de tests a le droit de poser des questions à l’utilisateur durant son exécution. Par exemple, elle pourrait demander la permission de supprimer une base de données de test existante. Siinteractive
vautFalse
, la suite de tests doit pouvoir s’exécuter sans aucune intervention manuelle de l’utilisateur.Si
failfast
vautTrue
, la suite de tests s’arrête immédiatement après le premier échec de test.Si
keepdb
vautTrue
, la suite de tests va utiliser la base de données existante ou en créer une si c’est nécessaire. Si la valeur estFalse
, une nouvelle base de données est toujours créée, demandant à l’utilisateur de supprimer une éventuelle base existante de même nom.Si
reverse
vautTrue
, les cas de tests sont exécutés dans l’ordre inverse. Cela peut être utile pour déboguer des tests qui ne sont pas isolés correctement et génèrent des effets de bord. Le groupement par classe de test est conservé lors de l’utilisation de cette option.debug_mode
indique la valeur que doit prendre le réglageDEBUG
avant d’exécuter les tests.Si
debug_sql
vautTrue
, les tests en échec affichent les requêtes SQL journalisées vers django.db.backends en plus de la pile d’appels. Siverbosity
vaut2
, les requêtes de tous les tests sont affichées.Il est possible que de temps à autre, Django étende les capacités du lanceur de tests en ajoutant de nouveaux paramètres. La déclaration
**kwargs
permet cette extension. Si vous héritez deDiscoverRunner
ou que vous écrivez votre propre lanceur de tests, vérifiez qu’il accepte**kwargs
.Votre lanceur de tests peut aussi définir des options supplémentaires en ligne de commande. Créez ou surchargez une méthode de classe
add_arguments(cls, parser)
et ajoutez des paramètres personnalisés en appelantparser.add_argument()
dans cette méthode, afin que la commandetest
puisse exploiter ces paramètres.
Attributs¶
-
DiscoverRunner.
test_suite
¶ La classe utilisée pour construire la suite de tests. Par défaut, il s’agit de
unittest.TestSuite
. Il est possible de surcharger cette valeur si vous souhaitez implémenter une logique différente pour collecter les tests.
-
DiscoverRunner.
test_runner
¶ Cette classe est celle du lanceur de tests de bas niveau utilisé pour exécuter les tests individuels et mettre en forme les résultats. Par défaut, il s’agit de
unittest.TextTestRunner
. Malgré la malencontreuse similitude des conventions de nommage, il ne s’agit pas du même type de classe queDiscoverRunner
, qui prend en charge un plus grand spectre de responsabilités. Vous pouvez surcharger cet attribut pour modifier la façon dont les tests sont exécutés et leurs résultats affichés.
-
DiscoverRunner.
test_loader
¶ Il s’agit de la classe qui charge les tests, que ce soit à partir de classes
TestCase
, de modules ou d’autres façons, et qui les consolide en suites de tests que le lanceur pourra exécuter. Par défaut, c’estunittest.defaultTestLoader
. Vous pouvez surcharger cet attribut si vos tests doivent être chargés de manière inhabituelle.
Méthodes¶
-
DiscoverRunner.
run_tests
(test_labels, extra_tests=None, **kwargs)[source]¶ Lance la suite de tests.
test_labels
permet d’indiquer les tests à exécuter et accepte plusieurs formats (voirDiscoverRunner.build_suite()
pour la liste des formats pris en charge).extra_tests
est une liste d’instancesTestCase
supplémentaires à ajouter à la suite exécutée par le lanceur de tests. Ces tests supplémentaires sont exécutés en plus de ceux découverts dans les modules énumérés danstest_labels
.Cette méthode doit renvoyer le nombre de tests ayant échoué.
-
classmethod
DiscoverRunner.
add_arguments
(parser)[source]¶ Surchargez cette méthode de classe pour ajouter des paramètres personnalisés acceptés par la commande d’administration
test
. Voirargparse.ArgumentParser.add_argument()
pour plus de détails au sujet de l’ajout de paramètres de ligne de commande.
-
DiscoverRunner.
setup_test_environment
(**kwargs)[source]¶ Met en place l’environnement de test en appelant
setup_test_environment()
et en définissantDEBUG
àself.debug_mode
(False
par défaut).
-
DiscoverRunner.
build_suite
(test_labels, extra_tests=None, **kwargs)[source]¶ Construit une suite de tests correspondant aux noms de test indiqués.
test_labels
est une liste de chaînes décrivant les tests à exécuter. Un nom de test peut apparaître sous quatre formes :chemin.vers.module_test.TestCase.methode_test
– Lance une seule méthode de test dans un cas de test.chemin.vers.module_test.TestCase
– Lance toutes les méthodes de test d’un cas de test.chemin.vers.module
– Recherche et lance tous les tests d’un paquet ou module Python donné.chemin/vers/répertoire
– Recherche et lance tous les tests dans la sous-arborescence du répertoire donné.
Si
test_labels
contient la valeurNone
, le lanceur de tests recherche des tests dans tous les fichiers au-dessous du répertoire actuel dont le nom correspond à son motifpattern
(voir ci-dessus).extra_tests
est une liste d’instancesTestCase
supplémentaires à ajouter à la suite exécutée par le lanceur de tests. Ces tests supplémentaires sont exécutés en plus de ceux découverts dans les modules énumérés danstest_labels
.Renvoie une instance
TestSuite
prête à être exécutée.
-
DiscoverRunner.
setup_databases
(**kwargs)[source]¶ Crée les bases de données de test en appelant
setup_databases()
.
-
DiscoverRunner.
run_checks
()[source]¶ Lance les vérifications systèmes.
-
DiscoverRunner.
run_suite
(suite, **kwargs)[source]¶ Lance la suite de tests.
Renvoie le résultat produit par l’exécution de la suite de tests.
-
DiscoverRunner.
get_test_runner_kwargs
()[source]¶ Renvoie les paramètres nommés pour l’instanciation de
DiscoverRunner.test_runner
.
-
DiscoverRunner.
teardown_databases
(old_config, **kwargs)[source]¶ Supprime les bases de données de test, rétablissant la situation d’avant-test en appelant
teardown_databases()
.
Utilitaires de test¶
django.test.utils
¶
Pour aider à la création de votre propre lanceur de tests, Django fournit un certain nombre de méthodes utilitaires dans le module django.test.utils
.
-
setup_test_environment
(debug=None)[source]¶ Effectue la mise en place générale avant les tests, comme l’installation des adaptations du système de rendu des gabarits et la mise en place de la boîte de messagerie factice.
Si
debug
ne vaut pasNone
, le réglageDEBUG
est mis à jour avec cette valeur.
-
teardown_test_environment
()[source]¶ Effectue la finalisation générale après les tests, comme la suppression des adaptations du système des gabarits et le rétablissement des services de messagerie par défaut.
-
setup_databases
(verbosity, interactive, keepdb=False, debug_sql=False, parallel=0, **kwargs)[source]¶ Crée les bases de données de test.
Renvoie une structure de données fournissant suffisamment de détails pour annuler les changements effectués. Ces données sont ensuite transmises à la fonction
teardown_databases()
lors de la finalisation des tests.
-
teardown_databases
(old_config, parallel=0, keepdb=False)[source]¶ Supprime les bases de données de test, rétablissant la situation d’avant-test.
old_config
est une structure de données définissant les modifications de la configuration des bases de données qui doivent être annulées. C’est la valeur de renvoi de la méthodesetup_databases()
.
django.db.connection.creation
¶
Le module de création du moteur de base de données propose également quelques utilitaires pouvant être utiles durant les tests.
-
create_test_db
(verbosity=1, autoclobber=False, serialize=True, keepdb=False)¶ Crée une nouvelle base de données de test et exécute
migrate
sur cette base.verbosity
a le même effet que dansrun_tests()
.autoclobber
décrit le comportement adopté si une base de données de même nom que la base de données de test existe déjà :- Si
autoclobber
vautFalse
, l’utilisateur devra confirmer la suppression de la base de données existante.sys.exit
est appelée si l’utilisateur refuse. - Si
autoclobber
vautTrue
, la base de données est supprimée sans rien demander à l’utilisateur.
serialize
détermine si Django sérialise la base de données en une chaîne JSON en mémoire avant de lancer les tests (utilisé pour restaurer l’état de la base de données entre les tests s’il n’y a pas de transactions). Vous pouvez le définir àFalse
pour accélérer nettement le temps de création si aucune de vos classes de test ne comporte serialized_rollback=True.Si vous utilisez le lanceur de tests par défaut, vous pouvez contrôler cela avec la clé
SERIALIZE
du dictionnaireTEST
.keepdb
détermine si la session de test doit utiliser une base de données existante ou si elle doit en créer une nouvelle. SiTrue
, une éventuelle base de données existante est utilisée, sinon une nouvelle base est créée. SiFalse
, une nouvelle base de données sera créée dans tous les cas, en demandant à l’utilisateur s’il doit supprimer la base existante, s’il y en a une.Renvoie le nom de la base de données de test qui a été créée.
create_test_db()
présente un effet de bord : la valeur deNAME
dansDATABASES
est mise à jour pour correspondre au nom de la base de données de test.- Si
-
destroy_test_db
(old_database_name, verbosity=1, keepdb=False)¶ Supprime la base de données dont le nom correspond à la valeur de
NAME
dansDATABASES
et met à jourNAME
avec la valeur contenue dansold_database_name
.Le paramètre
verbosity
a le même effet que pourDiscoverRunner
.Si le paramètre
keepdb
vautTrue
, la connexion à la base de données sera fermée, mais la base de données ne sera pas détruite.
Intégration avec coverage.py
¶
La couverture de code représente la quantité de code source mis à l’épreuve par les tests. Elle montre quelles sont les parties de code que les tests éprouvent et les parties qui ne le sont pas. C’est un aspect important du test des applications, c’est pourquoi il est fortement recommandé de contrôler la couverture de vos tests.
Django peut facilement s’intégrer à coverage.py, un outil de mesure de couverture du code de programmes Python. Premièrement, installez coverage.py. Puis, lancez la commande suivante à partir du répertoire de votre projet contenant manage.py
:
coverage run --source='.' manage.py test myapp
Cela va lancer les tests et collecter les données de couverture des fichiers exécutés dans votre projet. Vous pouvez afficher un rapport de ces données en saisissant la commande suivante :
coverage report
Notez que du code Django a été exécuté lors de l’exécution des tests, mais il ne figure pas dans le rapport à cause de l’option source
indiquée dans la commande précédente.
Pour davantage d’options comme les listings HTML annotés détaillant les lignes manquantes, consultez la documentation de coverage.py.