Thématiques de tests avancées

La fabrique de requêtes

class RequestFactory

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() et trace().
  • 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 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)

AsyncRequestFactory

class AsyncRequestFactory

RequestFactory crée des requêtes de type WSGI. Si vous souhaitez créer des requêtes de type ASGI, ayant aussi une portée scope ASGI correcte, vous pouvez utiliser django.test.AsyncRequestFactory à la place.

L’API de cette classe est directement compatible avec RequestFactory, avec comme seule différence le renvoi d’instances ASGIRequest au lieu de WSGIRequest. Toutes ses méthodes restent des exécutables synchrones.

Arbitrary keyword arguments in defaults are added directly into the ASGI scope.

Test des vues fondées sur les classes

Afin de tester les vues fondées sur des classes en dehors du cycle requête/réponse, vous devez vous assurer qu’elles sont configurées correctement en appelant setup() après leur instanciation.

Par exemple, en partant de la vue classe suivante :

views.py
from django.views.generic import TemplateView


class HomeView(TemplateView):
    template_name = 'myapp/home.html'

    def get_context_data(self, **kwargs):
        kwargs['environment'] = 'Production'
        return super().get_context_data(**kwargs)

Vous pouvez directement tester la méthode get_context_data() en commençant par créer une instance de la vue, puis en passant une requête à setup() avant de procéder au test du code :

tests.py
from django.test import RequestFactory, TestCase
from .views import HomeView


class HomePageTest(TestCase):
    def test_environment_set_in_context(self):
        request = RequestFactory().get('/')
        view = HomeView()
        view.setup(request)

        context = view.get_context_data()
        self.assertIn('environment', context)

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.

La première option 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.

When the test environment is configured, a test version of replica will not be created. Instead the connection to replica will be redirected to point at default. As a result, writes to default will appear on replica – but because they are actually the same database, not because there is data replication between the two databases. As this depends on transactions, the tests must use TransactionTestCase instead of TestCase.

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 vaut None. Après chaque test, Django appelle flush pour réinitialiser l’état de la base de données. Toutes les tables sont ainsi vidées et le signal post_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 de TransactionTestCase 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 test TransactionTestCase, 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 un TransactionTestCase n’est pas le même qu’après un TestCase: les lignes créées par les récepteurs de post_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 tests TransactionTestCase d’une suite de tests déclarent available_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 classe TransactionTestCase, 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.

Exécution séquentielle forcée des classes de tests

Si vous avez des classes de tests qui ne peuvent pas fonctionner en parallèle (par exemple si elles partagent une ressource commune), vous pouvez utiliser django.test.testcases.SerializeMixin pour les exécuter de manière séquentielle. Cette classe mixin utilise un fichier verrou lockfile du système de fichiers.

Par exemple, vous pouvez utiliser __file__ pour déterminer que toutes les classes de tests du même fichier héritant de SerializeMixin seront exécutées les unes à la suite des autres

import os

from django.test import TestCase
from django.test.testcases import SerializeMixin

class ImageTestCaseMixin(SerializeMixin):
    lockfile = __file__

    def setUp(self):
        self.filename = os.path.join(temp_storage_dir, 'my_file.png')
        self.file = create_file(self.filename)

class RemoveImageTests(ImageTestCaseMixin, TestCase):
    def test_remove_image(self):
        os.remove(self.filename)
        self.assertFalse(os.path.exists(self.filename))

class ResizeImageTests(ImageTestCaseMixin, TestCase):
    def test_resize_image(self):
        resize_image(self.file, (48, 48))
        self.assertEqual(get_image_size(self.file), (48, 48))

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 :

runtests.py
#!/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.

tests/test_settings.py
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 :

  1. Une préparation générale avant les tests.
  2. La recherche de tests dans tout fichier au-dessous du répertoire actuel dont le nom correspond au motif test*.py.
  3. La création des bases de données de test.
  4. Le lancement de migrate pour installer les modèles et les données initiales dans les bases de données de test.
  5. L’exécution des vérifications systèmes.
  6. L’exécution des tests découverts.
  7. La suppression des bases de données de test.
  8. 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, parallel=0, tags=None, exclude_tags=None, test_name_patterns=None, pdb=False, buffer=False, enable_faulthandler=True, timing=True, shuffle=False, logger=None, **kwargs)

DiscoverRunner recherche des tests dans tout fichier correspondant au motif pattern.

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 fichier manage.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 et 2 à l’affichage verbeux.

Si interactive vaut True, 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. Si interactive vaut False, la suite de tests doit pouvoir s’exécuter sans aucune intervention manuelle de l’utilisateur.

Si failfast vaut True, la suite de tests s’arrête immédiatement après le premier échec de test.

Si keepdb vaut True, 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 est False, 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 vaut True, 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. Celle-ci peut être utilisée conjointement avec --shuffle pour inverser l’ordre d’une graine aléatoire particulière.

debug_mode indique la valeur que doit prendre le réglage DEBUG avant d’exécuter les tests.

parallel indique le nombre de processus. Si parallel est plus grand que 1, la suite de tests s’exécutera dans des processus en parallèle. S’il y a moins de cas de test que de processus configurés, Django se charge de réduire le nombre de processus en conséquence. Chaque processus reçoit sa propre base de données. Cette option nécessite la présence du paquet tiers tblib pour afficher correctement les traces d’erreurs.

tags peut être utilisé pour définir un jeu d’étiquettes pour le filtrage des tests. Peut être combiné avec exclude_tags.

exclude_tags peut être utilisé pour définir un jeu d’étiquettes pour l’exclusion de tests. Peut être combiné avec tags.

Si debug_sql vaut True, les tests en échec affichent les requêtes SQL journalisées vers django.db.backends en plus de la pile d’appels. Si verbosity vaut 2, les requêtes de tous les tests sont affichées.

test_name_patterns peut être utilisé pour définir un ensemble de motifs pour filtrer les méthodes et classes de test par leur nom.

Si pdb vaut True, un débogueur (pdb ou ipdb) sera déclenché lors de chaque erreur ou échec de test.

Si buffer vaut True, la sortie des tests réussis sera omise.

Si enable_faulthandler vaut True, faulthandler sera activé.

Si timing vaut True, la mesure temporelle des tests sera affichée, y compris la mise en place de la base de données et le temps total d’exécution.

Si shuffle est un nombre entier, les cas de test seront mélangés aléatoirement avant leur exécution, en utilisant le nombre entier comme facteur aléatoire. Si shuffle vaut None, ce facteur sera généré aléatoirement. Dans les deux cas, le facteur sera journalisé et défini dans self.shuffle_seed avant de lancer les tests. Cette option peut être utilisée pour aider à détecter les tests qui ne sont pas proprement isolés. Le groupement par classe de test est conservé avec cette option.

logger peut être utilisé pour passer un objet Logger Python. Si indiqué, ce journaliseur sera utilisé pour journaliser les messages au lieu de les afficher dans la console. Cet objet journaliseur respectera son niveau de journalisation plutôt que le paramètre verbosity.

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 de DiscoverRunner 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 appelant parser.add_argument() dans cette méthode, afin que la commande test puisse exploiter ces paramètres.

New in Django 4.0:

Les paramètres logger et shuffle ont été ajoutés.

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 que DiscoverRunner, 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’est unittest.defaultTestLoader. Vous pouvez surcharger cet attribut si vos tests doivent être chargés de manière inhabituelle.

Méthodes

DiscoverRunner.run_tests(test_labels, **kwargs)

Lance la suite de tests.

test_labels permet d’indiquer les tests à exécuter et accepte plusieurs formats (voir DiscoverRunner.build_suite() pour la liste des formats pris en charge).

Obsolète depuis la version 4.0: extra_tests est une liste d’instances TestCase 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 dans test_labels.

Cette méthode doit renvoyer le nombre de tests ayant échoué.

classmethod DiscoverRunner.add_arguments(parser)

Surchargez cette méthode de classe pour ajouter des paramètres personnalisés acceptés par la commande d’administration test. Voir argparse.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)

Met en place l’environnement de test en appelant setup_test_environment() et en définissant DEBUG à self.debug_mode (False par défaut).

DiscoverRunner.build_suite(test_labels=None, **kwargs)

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 valeur None, le lanceur de tests recherche des tests dans tous les fichiers au-dessous du répertoire actuel dont le nom correspond à son motif pattern (voir ci-dessus).

Obsolète depuis la version 4.0: extra_tests est une liste d’instances TestCase 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 dans test_labels.

Renvoie une instance TestSuite prête à être exécutée.

DiscoverRunner.setup_databases(**kwargs)

Crée les bases de données de test en appelant setup_databases().

DiscoverRunner.run_checks(databases)

Lance les vérifications systèmes pour les bases databases de test.

DiscoverRunner.run_suite(suite, **kwargs)

Lance la suite de tests.

Renvoie le résultat produit par l’exécution de la suite de tests.

DiscoverRunner.get_test_runner_kwargs()

Renvoie les paramètres nommés pour l’instanciation de DiscoverRunner.test_runner.

DiscoverRunner.teardown_databases(old_config, **kwargs)

Supprime les bases de données de test, rétablissant la situation d’avant-test en appelant teardown_databases().

DiscoverRunner.teardown_test_environment(**kwargs)

Rétablit l’environnement à son état d’avant les tests.

DiscoverRunner.suite_result(suite, result, **kwargs)

Calcule et renvoie un code de retour basé sur une suite de tests et des résultats de ces tests.

DiscoverRunner.log(msg, level=None)
New in Django 4.0.

Quand le paramètre logger est défini, journalise le message au niveau de journalisation donné (nombre entier), par ex. logging.DEBUG, logging.INFO ou logging.WARNING. Sinon, le message est affiché dans la console en respectant le paramètre verbosity en cours. Par exemple, aucun message ne sera affiché si verbosity vaut 0, les niveaux INFO et supérieurs seront affichés si verbosity vaut au moins 1, et les niveaux DEBUG seront affichés si verbosity vaut au moins 2. La valeur par défaut de level est logging.INFO.

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)

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 pas None, le réglage DEBUG est mis à jour avec cette valeur.

teardown_test_environment()

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, *, time_keeper=None, keepdb=False, debug_sql=False, parallel=0, aliases=None, serialized_aliases=None, **kwargs)

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.

Le paramètre aliases détermine pour quels alias de DATABASES les bases de données de test doivent être préparées. S’il n’est pas présent, sa valeur par défaut contient tous les alias de DATABASES.

Le paramètre serialized_aliases détermine le sous-ensemble des aliases de bases de données de test qui devraient avoir leur état sérialisé pour permettre l’utilisation de la fonctionnalité serialized_rollback. Si non défini, la valeur par défaut est aliases.

Changed in Django 4.0:

Le paramètre mot-clé serialized_aliases a été ajouté.

teardown_databases(old_config, parallel=0, keepdb=False)

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éthode setup_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 dans run_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 vaut False, l’utilisateur devra confirmer la suppression de la base de données existante. sys.exit est appelée si l’utilisateur refuse.
  • Si autoclobber vaut True, 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 dictionnaire TEST.

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. Si True, une éventuelle base de données existante est utilisée, sinon une nouvelle base est créée. Si False, 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 de NAME dans DATABASES est mise à jour pour correspondre au nom de la base de données de test.

destroy_test_db(old_database_name, verbosity=1, keepdb=False)

Supprime la base de données dont le nom correspond à la valeur de NAME dans DATABASES et met à jour NAME avec la valeur contenue dans old_database_name.

Le paramètre verbosity a le même effet que pour DiscoverRunner.

Si le paramètre keepdb vaut True, 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.

Back to Top