# Documentation Django 5.2.8

# Outils de test

Django propose un petit set d’outils bien pratiques lors de l’écriture de tests.

## Le client de test

Le client de test est une classe Python se comportant comme un navigateur web simpliste, permettant de tester les vues et d’interagir par programmation avec votre application Django.

Voici plusieurs choses que vous pouvez faire avec le client de test :

- Simuler des requêtes GET et POST sur une URL et examiner la réponse, que ce soit les détails HTTP de bas niveau (en-têtes de la réponse et codes d’état) ou le contenu de la page renvoyée.

- Voir la chaîne des redirections (le cas échéant) et contrôler l’URL et le code de statut à chaque étape.

- Tester qu’une requête données est rendue par un gabarit Django donné, et que le contexte du gabarit contient certaines valeurs.

Notez que le client de test n’est pas conçu pour remplacer Selenium ou d’autres systèmes utilisant un navigateur réel. Le client de test de Django a un objectif différent. En bref :

- Utiliser le client de test de Django pour confirmer que le gabarit correct a été utilisé pour le rendu et que ce gabarit à reçu les bonnes données de contexte.

- Utilisez `RequestFactory` pour tester directement les fonctions de vues, outrepassant les couches de routage et d’intergiciel.

- Utilisez des systèmes basés sur de vrais navigateurs comme Selenium pour tester le HTML produit et le comportement des pages web, en particulier les fonctionnalités JavaScript. Django offre aussi une prise en charge spéciale pour ces systèmes ; consultez la section sur `LiveServerTestCase` pour plus de détails.

Une suite de tests complète devrait utiliser une combinaison de tous ces types de tests.

### Aperçu et exemple rapide

Pour utiliser le client de test, créez une instance de `django.test.Client` et récupérez des pages web :

    >>> from django.test import Client
    >>> c = Client()
    >>> response = c.post("/login/", {"username": "john", "password": "smith"})
    >>> response.status_code
    200
    >>> response = c.get("/customer/details/")
    >>> response.content
    b'<!DOCTYPE html...'

Comme cet exemple le suggère, vous pouvez créer des instances de `Client` à partir d’une session de l’interpréteur Python interactif.

Signalons quelques éléments importants au sujet du fonctionnement du client de test :

- Le client de test n’a pas besoin que le serveur web soit lancé. En fait, il fonctionne tout à fait correctement sans aucun serveur web actif ! Et cela parce qu’il s’adresse directement à l’infrastructure Django sans passer par la couche HTTP. Cela permet d’accélérer le fonctionnement des tests unitaires.

- Lors de la récupération des pages, n’oubliez pas de n’indiquer que le chemin de l’URL, sans mentionner tout le nom de domaine. Par exemple, ceci est correct :

      >>> c.get("/login/")

  Mais pas ceci :

      >>> c.get("https://www.example.com/login/")

  Le client de test n’est pas capable de récupérer des pages web qui ne sont pas basées sur votre projet Django. Si vous avez besoin de récupérer d’autres pages web, utilisez un module standard de la bibliothèque Python, comme `urllib`.

- Pour résoudre des URL, le client de test utilise la configuration d’URL qui est désignée par le réglage `ROOT_URLCONF`.

- Même si l’exemple ci-dessus fonctionne dans un interpréteur Python interactif, certaines fonctionnalités du client de test, notamment celles liées aux gabarits, ne sont disponibles que lorsque les tests sont lancés.

  Ceci s’explique par le fait que le lanceur de tests de Django fait sa propre cuisine pour déterminer quel gabarit a été chargé pour une vue donnée. Cette « cuisine » (essentiellement un correctif en mémoire du système de gabarits de Django) n’est opérée que durant le fonctionnement des tests.

- Par défaut, le client de test désactive tous les contrôles CSRF effectués par votre site.

  Si pour une raison quelconque vous * voulez* que le client de test effectue les contrôles CSRF, vous pouvez créer une instance du client de test avec les contrôles CSRF actifs. Pour cela, passez le paramètre `enforce_csrf_checks` au moment de créer le client :

      >>> from django.test import Client
      >>> csrf_client = Client(enforce_csrf_checks=True)

### Lancement de requêtes

La classe `django.test.Client` permet de simuler des requêtes HTTP.

class Client(enforce_csrf_checks=False, raise_request_exception=True, json_encoder=DjangoJSONEncoder, *, headers=None, query_params=None, **defaults)

  Un client de test HTTP. Accepte plusieurs arguments qui peuvent adapter son comportement.

  `headers` permet d’indiquer des en-têtes par défaut qui seront envoyés avec chaque requête. Par exemple, pour défini un en-tête `User-Agent`:

      client = Client(headers={"user-agent": "curl/7.79.1"})

  `query_params` permet d’indiquer la chaîne de requête par défaut qui sera définie pour chaque requête.

  Des paramètres nommés arbitraires dans `**defaults` définissent des variables d’environnement WSGI. Par exemple, pour définir le nom du script

      client = Client(SCRIPT_NAME="/app/")

  Note

  Les paramètres nommés commençant par un préfixe `HTTP_` sont définis comme des en-têtes, mais le paramètre `headers` devrait être privilégié pour une meilleure lisibilité.

  The values from the `headers`, `query_params`, and `extra` keyword arguments passed to `get()`, `post()`, etc. have precedence over the defaults passed to the class constructor.

  Le paramètre `enforce_csrf_checks` peut être utilisé pour tester la protection CSRF (voir au-dessus).

  Le paramètre `raise_request_exception` permet de contrôler si les exceptions générées pendant la requête doivent aussi être générées dans le test. Vaut `True` par défaut.

  Le paramètre `json_encoder` permet de définir un codeur JSON personnalisé pour la sérialisation JSON décrite dans `post()`.

  Dès que vous avez à disposition une instance de `Client`, vous pouvez appeler au choix l’une des méthodes suivantes :

  get(path, data=None, follow=False, secure=False, *, headers=None, query_params=None, **extra)

    Procède à une requête GET utilisant le chemin `path` indiqué et renvoie un objet `Response`, qui est documenté plus bas.

    Les paires clé-valeur dans le dictionnaire `query_params` servent à définir les chaînes de requête. Par exemple :

        >>> c = Client()
        >>> c.get("/customers/details/", query_params={"name": "fred", "age": 7})

    …aboutit à l’évaluation d’une requête GET équivalente à :

        /customers/details/?name=fred&age=7

    Il est aussi possible de transmettre ces paramètres dans le paramètre `data`. Cependant, `query_params` devrait être préféré car il fonctionne pour n’importe quelle méthode HTTP.

    Le paramètre `headers` peut être utilisé pour indiquer les en-têtes envoyés avec la requête. Par exemple :

        >>> c = Client()
        >>> c.get(
        ...     "/customers/details/",
        ...     query_params={"name": "fred", "age": 7},
        ...     headers={"accept": "application/json"},
        ... )

    …will send the HTTP header `HTTP_ACCEPT` to the details view, which is a good way to test code paths that use the `django.http.HttpRequest.accepts()` method.

    Des paramètres nommés arbitraires définissent des variables d’environnement WSGI. Par exemple, pour définir le nom du script :

        >>> c = Client()
        >>> c.get("/", SCRIPT_NAME="/app/")

    Si vous disposez déjà des paramètres GET sous une forme déjà codée pour l’URL, vous pouvez utiliser cette forme au lieu du paramètre `data`. Par exemple, la requête GET précédente pourrait aussi être émise par :

        >>> c = Client()
        >>> c.get("/customers/details/?name=fred&age=7")

    Si vous fournissez à la fois une URL contenant des données GET codées et un paramètre `data` ou un paramètre `query_params`, ces paramètres auront la priorité.

    Si vous définissez `follow` à `True`, le client suit d’éventuelles redirections et l’objet réponse possédera un attribut `redirect_chain` contenant des tuples formés des URL intermédiaires et de leur code d’état.

    Si vous aviez une URL `/redirect_me/` redirigeant vers `/next/`, redirigeant lui-même vers `/final/`, voici ce que vous obtiendriez :

        >>> response = c.get("/redirect_me/", follow=True)
        >>> response.redirect_chain
        [('http://testserver/next/', 302), ('http://testserver/final/', 302)]

    Si vous définissez `secure` à `True`, le client émule une requête HTTPS.

  post(path, data=None, content_type=MULTIPART_CONTENT, follow=False, secure=False, *, headers=None, query_params=None, **extra)

    Procède à une requête POST utilisant le chemin `path` indiqué et renvoie un objet `Response`, qui est documenté plus bas.

    Les paires clé-valeur dans le dictionnaire `data` servent à créer les données de soumission POST. Par exemple :

        >>> c = Client()
        >>> c.post("/login/", {"name": "fred", "passwd": "secret"})

    …aboutit à l’évaluation d’une requête POST vers cette URL :

        /login/

    …contenant ces données POST :

        name=fred&passwd=secret

    Si vous indiquez application/json dans `content_type`, les données `data` sont sérialisées en utilisant `json.dumps()` s’il s’agit d’un dict, d’une liste ou d’un tuple. La sérialisation est appliquée par défaut avec `DjangoJSONEncoder`, mais cette classe peut être remplacée en fournissant le paramètre `json_encoder` à `Client`. Cette sérialisation se produit également pour les requêtes `put()`, `patch()` et `delete()`.

    Si vous renseignez tout autre `content_type` (par ex. text/xml pour des données utiles XML), le contenu de `data` est envoyé tel quel dans la requête POST, en plaçant le contenu de `content_type` dans l’en-tête HTTP `Content-Type`.

    Si le paramètre `content_type` n’est pas renseigné, les valeurs contenues dans `data` sont transmises avec un type de contenu multipart/form-data. Dans ce cas, les paires clé-valeur de `data` sont codées sous forme de message composite (`multipart`) et servent à créer les données utiles de POST.

    Pour envoyer plusieurs valeurs pour un même clé, par exemple pour indiquer les sélections d’un élément `<select multiple>`, indiquez les valeurs de la clé sous forme de liste ou de tuple. Par exemple, le contenu suivant de `data` envoie trois valeurs sélectionnées pour le champ nommé `choices`:

        {"choices": ["a", "b", "d"]}

    L’envoi de fichiers est un cas particulier. Pour envoyer un fichier par POST, il suffit d’indiquer comme clé le nom du champ de fichier et comme valeur un pointeur de fichier référençant le fichier à envoyer. Par exemple, si le formulaire possède les champs `name` et `attachment`, ce dernier étant un `FileField`:

        >>> c = Client()
        >>> with open("wishlist.doc", "rb") as fp:
        ...     c.post("/customers/wishes/", {"name": "fred", "attachment": fp})
        ...

    Il est aussi possible de fournir un objet de type fichier (par ex. `StringIO` ou `BytesIO`) comme pointeur de fichier. Si le fichier est destiné à un champ `ImageField`, l’objet a besoin d’un attribut `name` passant la validation de `validate_image_file_extension`. Par exemple :

        >>> from io import BytesIO
        >>> img = BytesIO(
        ...     b"GIF89a\x01\x00\x01\x00\x00\x00\x00!\xf9\x04\x01\x00\x00\x00"
        ...     b"\x00,\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x01\x00\x00"
        ... )
        >>> img.name = "myimage.gif"

    Notez que si vous souhaitez utiliser le même pointeur de fichier pour plusieurs appels à `post()`, vous devrez manuellement réinitialiser ce pointeur entre les requêtes. La façon la plus simple de le faire manuellement est de fermer le fichier après qu’il a été fourni à `post()`, comme cela est fait dans l’exemple ci-dessus.

    Vous devez aussi être certain que le fichier est ouvert d’une manière autorisant les données à être lues. Si votre fichier contient des données binaires telles qu’une image, cela signifie que vous devrez ouvrir le fichier en mode `rb` (read binary).

    Les paramètres `headers`, `query_params` et `extra` jouent le même rôle que pour `Client.get()`.

    Si l’URL indiquée pour la requête POST contient des paramètres codés, ceux-ci sont placés dans les données `request.GET`. Par exemple, si vous effectuez la requête :

        >>> c.post(
        ...     "/login/", {"name": "fred", "passwd": "secret"}, query_params={"visitor": "true"}
        ... )

    …la vue traitant la requête peut consulter `request.POST` pour obtenir les données du nom et du mot de passe, et `request.GET` pour savoir si l’utilisateur est un visiteur.

    Si vous définissez `follow` à `True`, le client suit d’éventuelles redirections et l’objet réponse possédera un attribut `redirect_chain` contenant des tuples formés des URL intermédiaires et de leur code d’état.

    Si vous définissez `secure` à `True`, le client émule une requête HTTPS.

  head(path, data=None, follow=False, secure=False, *, headers=None, query_params=None, **extra)

    Procède à une requête HEAD utilisant le chemin `path` indiqué et renvoie un objet `Response`. Cette méthode fonctionne comme `Client.get()`, y compris les paramètres `follow`, `secure`, `headers`, `query_params` et `extra` à la seule exception qu’aucun corps de message n’est renvoyé.

  options(path, data='', content_type='application/octet-stream', follow=False, secure=False, *, headers=None, query_params=None, **extra)

    Procède à une requête OPTIONS utilisant le chemin `path` indiqué et renvoie un objet `Response`. Utile pour tester les interfaces de type « REST ».

    Lorsque `data` est renseigné, il est utilisé comme corps de requête et un en-tête `Content-Type` est défini avec le contenu de `content_type`.

    Les paramètres `follow`, `secure`, `headers`, `query_params` et `extra` jouent le même rôle que pour `Client.get()`.

  put(path, data='', content_type='application/octet-stream', follow=False, secure=False, *, headers=None, query_params=None, **extra)

    Procède à une requête PUT utilisant le chemin `path` indiqué et renvoie un objet `Response`. Utile pour tester les interfaces de type « REST ».

    Lorsque `data` est renseigné, il est utilisé comme corps de requête et un en-tête `Content-Type` est défini avec le contenu de `content_type`.

    Les paramètres `follow`, `secure`, `headers`, `query_params` et `extra` jouent le même rôle que pour `Client.get()`.

  patch(path, data='', content_type='application/octet-stream', follow=False, secure=False, *, headers=None, query_params=None, **extra)

    Procède à une requête PATCH utilisant le chemin `path` indiqué et renvoie un objet `Response`. Utile pour tester les interfaces de type « REST ».

    Les paramètres `follow`, `secure`, `headers`, `query_params` et `extra` jouent le même rôle que pour `Client.get()`.

  delete(path, data='', content_type='application/octet-stream', follow=False, secure=False, *, headers=None, query_params=None, **extra)

    Procède à une requête DELETE utilisant le chemin `path` indiqué et renvoie un objet `Response`. Utile pour tester les interfaces de type « REST ».

    Lorsque `data` est renseigné, il est utilisé comme corps de requête et un en-tête `Content-Type` est défini avec le contenu de `content_type`.

    Les paramètres `follow`, `secure`, `headers`, `query_params` et `extra` jouent le même rôle que pour `Client.get()`.

  trace(path, follow=False, secure=False, *, headers=None, query_params=None, **extra)

    Procède à une requête TRACE utilisant le chemin `path` indiqué et renvoie un objet `Response`. Utile pour simuler les tests diagnostics.

    Au contraire des autres méthodes de requête, `data` n’est pas fourni comme paramètre nommé afin de respecter la RFC 9110 Section 9.3.8, qui interdit aux requêtes TRACE de posséder un corps.

    Les paramètres `follow`, `secure`, `headers`, `query_params` et `extra` jouent le même rôle que pour `Client.get()`.

  login(**credentials)
  alogin(**credentials)

    Version asynchrone : `alogin()`

    Si un projet utilise le système d’authentification de Django et que les tests connectent des utilisateurs, il est possible d’utiliser la méthode `login()` du client de test pour simuler la connexion d’un utilisateur au site concerné.

    Après l’appel à cette méthode, le client de test contiendra tous les cookies et les données de session nécessaires pour passer les tests dans lesquels des vues comptent sur des utilisateurs connectés.

    Le format du paramètre `credentials` dépend du moteur d’authentification utilisé (configuré dans le réglage `AUTHENTICATION_BACKENDS`). Dans le cas du moteur d’authentification standard de Django (`ModelBackend`), `credentials` doit contenir le nom d’utilisateur et le mot de passe de l’utilisateur, fournis sous la forme de paramètres nommés :

        >>> c = Client()
        >>> c.login(username="fred", password="secret")

        # Now you can access a view that's only available to logged-in users.

    Si un autre moteur d’authentification est utilisé, cette méthode peut nécessiter un autre format de données d’authentification. Cela dépend des paramètres nécessaires à la méthode `authenticate()` du moteur en question.

    `login()` renvoie `True` si les données d’authentification ont été acceptées et que la connexion s’est terminée avec succès.

    Finally, you’ll need to remember to create user accounts before you can use this method. As we explained above, the test runner is executed using a test database, which contains no users by default. As a result, user accounts that are valid on your production site will not work under test conditions. You’ll need to create users as part of the test suite – either manually (using the Django model API) or with a test fixture. Remember that if you want your test user to have a password, you can’t set the user’s password by setting the password attribute directly – you must use the `set_password()` function to store a correctly hashed password. Alternatively, you can use the `create_user()` helper method to create a new user with a correctly hashed password.

  force_login(user, backend=None)
  aforce_login(user, backend=None)

    Version asynchrone : `aforce_login()`

    Si un projet utilise le système d’authentification de Django, il est possible d’utiliser la méthode `force_login()` pour simuler la connexion d’un utilisateur du site concerné. Il est avantageux d’utiliser cette méthode à la place de `login()` lorsqu’un test a besoin d’un utilisateur connecté mais que les détails de la manière dont l’utilisateur s’est connecté importent peu.

    Au contraire de `login()`, cette méthode omet les étapes d’authentification et de vérification : les utilisateurs inactifs (`is_active=False`) peuvent se connecter et il n’est pas nécessaire de fournir les informations d’authentification.

    L’attribut `backend` de l’utilisateur sera défini à la valeur du paramètre `backend` (qui devrait être un chemin Python pointé) ou à `settings.AUTHENTICATION_BACKENDS[0]` si ce paramètre n’est pas fourni. La fonction `authenticate()` appelée par `login()` annote normalement l’utilisateur de cette manière.

    Cette méthode est plus rapide que `login()` dans la mesure où les coûteux algorithmes de hachage de mot de passe sont ignorés. Notez qu’il est aussi possible d’accélérer `login()` en utilisant une méthode de hachage plus faible durant les tests.

  logout()
  alogout()

    Version asynchrone : `alogout()`

    Si un projet utilise le système d’authentification de Django, il est possible d’utiliser la méthode `logout()` du client de test pour simuler la déconnexion d’un utilisateur du site concerné.

    Après l’appel à cette méthode, le client de test verra toutes ses données de cookies et de session réinitialisées à leurs valeurs par défaut. Les requêtes suivantes apparaîtront comme si elles provenaient d’un utilisateur anonyme (`AnonymousUser`).

### Test des réponses

Les méthodes `get()` et `post()` renvoient les deux un objet `Response`. Cet objet n’est pas le même que les objets `HttpResponse` renvoyés par les vues Django ; l’objet de réponse de test possède des données supplémentaires bien utiles pour certaines vérifications dans le code des tests.

Plus précisément, un objet `Response` possède les attributs suivants :

class Response
  client

    Le client de test utilisé pour effectuer la requête qui a renvoyé cette réponse.

  content

    Le corps de la réponse sous forme de chaîne d’octets. Il s’agit du contenu final de la page produite par la vue, ou d’un éventuel message d’erreur.

  context

    L’instance `Context` du gabarit utilisé pour effectuer le rendu de gabarit qui a produit le contenu de la réponse.

    Si le rendu de la page à utilisé plusieurs gabarits, `context` contient une liste d’objets `Context` dans l’ordre de leur rendu.

    Quel que soit le nombre de gabarits utilisés dans le processus de rendu, vous pouvez récupérer les valeurs de contexte en utilisant l’opérateur `[]`. Par exemple, la variable de contexte `name` peut être récupérée ainsi :

        >>> response = client.get("/foo/")
        >>> response.context["name"]
        'Arthur'

    Vous n’utilisez pas les gabarits Django ?

    Cet attribut n’est présent que lorsque le moteur de gabarit est `DjangoTemplates`. Si vous utilisez un autre moteur, `context_data` peut constituer une alternative viable pour les réponses possédant cet attribut.

  exc_info

    Un tuple de trois valeurs fournissant des informations sur l’exception non traitée, le cas échéant, qui est apparue durant la vue.

    Les valeurs sont (type, valeur, trace d’erreur), tout comme ce que Python renvoie dans `sys.exc_info()`. Leur signification est :

    - type : le type de l’exception.

    - valeur : l’instance de l’exception.

    - trace d’erreur : Un objet `Traceback` qui englobe la pile d’appels au moment où l’exception a été initialement déclenchée.

    Si aucune exception ne s’est produite, `exc_info` contient `None`.

  json(**kwargs)

    Le corps de la réponse, analysée en JSON. Les paramètres nommés supplémentaires sont transmis à `json.loads()`. Par exemple :

        >>> response = client.get("/foo/")
        >>> response.json()["name"]
        'Arthur'

    Si l’en-tête `Content-Type` n’est pas `"application/json"`, une erreur `ValueError` sera signalée au moment d’analyser la réponse.

  request

    Les données de requête à l’origine de la réponse.

  wsgi_request

    L’instance `WSGIRequest` générée par le gestionnaire de test qui a produit la réponse.

  status_code

    Le statut HTTP de la réponse sous forme de nombre entier. Pour une liste exhaustive des codes définis, voir le registre IANA des codes de statut.

  templates

    Une liste d’instances de gabarits (`Template`) ayant servi à rendre le contenu final, dans l’ordre de leur utilisation. Pour chaque gabarit de la liste, utilisez `template.name` pour obtenir le nom de fichier du gabarit si celui-ci a été chargé à partir d’un fichier (le nom est une chaîne du genre `'admin/index.html'`).

    Vous n’utilisez pas les gabarits Django ?

    Cet attribut n’est présent que lorsque le moteur de gabarit est `DjangoTemplates`. Si vous utilisez un autre moteur, `template_name` peut constituer une alternative viable si vous n’avez besoin que du nom du gabarit utilisé pour le rendu.

  resolver_match

    Une instance de `ResolverMatch` pour la réponse. Vous pouvez utiliser l’attribut `func`, par exemple, pour vérifier la vue qui a servi la réponse :

        # my_view here is a function based view.
        self.assertEqual(response.resolver_match.func, my_view)

        # Class-based views need to compare the view_class, as the
        # functions generated by as_view() won't be equal.
        self.assertIs(response.resolver_match.func.view_class, MyView)

    Si l’URL indiquée n’est pas trouvée, l’accès à cet attribut génère l’exception `Resolver404`.

Comme pour les réponses normales, il est aussi possible d’accéder aux en-têtes par `HttpResponse.headers`. Par exemple, vous pouvez retrouver le type de contenu d’une réponse avec `response.headers['Content-Type']`.

### Exceptions

Si le client de test fait appel à une vue qui génère une exception et que `Client.raise_request_exception` vaut `True`, celle-ci est propagée dans le cas de test. Vous pouvez dès lors utiliser un bloc `try ... except` standard ou `assertRaises()` pour tester l’exception générée.

Les seules exceptions qui ne sont pas visibles pour le client de test sont `Http404`, `PermissionDenied`, `SystemExit` et `SuspiciousOperation`. Django intercepte en interne ces exceptions et les convertit en code de réponse HTTP adéquat. Dans ces situations, vous pouvez tester la valeur `response.status_code`.

Si `Client.raise_request_exception` vaut `False`, le client de test renverra une erreur 500 comme le ferait un navigateur. La réponse possède l’attribut `exc_info` pour fournir des informations sur l’exception non traitée.

### État persistant

L’état du client de test est persistant. Si une réponse renvoie un cookie, celui-ci est stocké dans le client de test et il sera ensuite envoyé dans les requêtes `get()` et `post()` subséquentes.

Les politiques d’expiration de ces cookies ne sont pas respectées. Si vous souhaitez faire expirer un cookie, supprimez-le manuellement ou créez une nouvelle instance de `Client` (ce qui aura comme conséquence de supprimer tous les cookies).

Un client de test possède des attributs qui stockent les informations d’état persistantes. Vous pouvez accéder à ces propriétés dans le cadre d’une condition de test.

Client.cookies

  Un objet Python `SimpleCookie` contenant les valeurs actuelles de tous les cookies du client. Consultez la documentation du module `http.cookies` pour en savoir plus.

Client.session

  Un objet de type dictionnaire contenant les informations de session. Consultez la documentation des sessions pour plus de détails.

  Pour modifier la session et l’enregistrer, elle doit être d’abord stockée dans une variable (car un nouveau `SessionStore` est créé chaque fois qu’on accède à cette propriété) :

      def test_something(self):
          session = self.client.session
          session["somekey"] = "test"
          session.save()

Client.asession()

  Ceci est comparable à l’attribut `session`, mais fonctionne en contexte asynchrone.

### Définition de la langue

Lors des tests d’applications qui prennent en charge l’internationalisation et la localisation, il peut être souhaitable de définir la langue de la requête du client de test. La méthode à employer dépend de l’activation ou non de `LocaleMiddleware`.

Si l’intergiciel est activé, la langue peut être définie en créant un cookie nommé `LANGUAGE_COOKIE_NAME` et une valeur contenant le code de langue :

    from django.conf import settings

    def test_language_using_cookie(self):
        self.client.cookies.load({settings.LANGUAGE_COOKIE_NAME: "fr"})
        response = self.client.get("/")
        self.assertEqual(response.content, b"Bienvenue sur mon site.")

ou en incluant l’en-tête HTTP `Accept-Language` dans la requête :

    def test_language_using_header(self):
        response = self.client.get("/", headers={"accept-language": "fr"})
        self.assertEqual(response.content, b"Bienvenue sur mon site.")

Note

Lorsque vous utilisez ces méthodes, prenez soin de réinitialiser la langue active à la fin de chaque test

    def tearDown(self):
        translation.activate(settings.LANGUAGE_CODE)

Voir Processus de découverte de la préférence de langue par Django pour plus de détails.

Si l’intergiciel n’est pas activé, la langue active peut être définie en utilisant `translation.override()`:

    from django.utils import translation

    def test_language_using_override(self):
        with translation.override("fr"):
            response = self.client.get("/")
        self.assertEqual(response.content, b"Bienvenue sur mon site.")

Voir Définition explicite de la langue active pour plus de détails.

### Exemple

L’exemple suivant est un test unitaire exploitant le client de test :

    import unittest
    from django.test import Client

    class SimpleTest(unittest.TestCase):
        def setUp(self):
            # Every test needs a client.
            self.client = Client()

        def test_details(self):
            # Issue a GET request.
            response = self.client.get("/customer/details/")

            # Check that the response is 200 OK.
            self.assertEqual(response.status_code, 200)

            # Check that the rendered context contains 5 customers.
            self.assertEqual(len(response.context["customers"]), 5)

Voir aussi

`django.test.RequestFactory`

## Classes de cas de test disponibles

Les classes de test unitaire normales de Python étendent la classe de base `unittest.TestCase`. Django fournit plusieurs extensions de cette classe de base :

Hiérarchie des classes de tests unitaires de Django

Il est possible de convertir une classe `unittest.TestCase` normale en une des sous-classes : remplacez la classe de base des tests `unittest.TestCase` par la sous-classe. Toutes les fonctionnalités standard de Python pour les tests unitaires sont toujours disponibles, et vous obtenez en plus quelques éléments utiles tels que documentés dans chaque section ci-dessous.

### `SimpleTestCase`

class SimpleTestCase

Une sous-classe de `unittest.TestCase` qui ajoute cette fonctionnalité :

- Certaines assertions utiles comme :

  - Vérification qu’un objet exécutable `génère une exception donnée`.

  - Vérification qu’un objet exécutable `génère un avertissement donné`.

  - Le test de `rendu et de traitement d'erreur` des champs de formulaire.

  - Le test de `présence ou d'absence d'un fragment donné dans des réponses HTML`.

  - La vérification qu’un gabarit `a été ou n'a pas été utilisé pour générer un contenu de réponse donné`.

  - La vérification que deux meth:URL <SimpleTestCase.assertURLEqual> sont égales.

  - La vérification qu’une `redirection HTTP` a été effectuée par une application.

  - La comparaison robuste entre deux `fragments HTML` ou la présence ou absence d’un `fragment HTML dans un autre`.

  - La comparaison robuste entre deux `fragments XML`.

  - La comparaison robuste entre deux `fragments JSON`.

- La capacité de lancer des tests avec des réglages modifiés.

- L’utilisation du `Client` `client`.

Si les tests effectuent des requêtes de base de données, utilisez les sous-classes `TransactionTestCase` ou `TestCase`.

SimpleTestCase.databases

  `SimpleTestCase` n’autorise pas de requête de base de données par défaut. Ceci permet d’éviter l’exécution de requêtes d’écriture qui pourraient affecter d’autres tests dans la mesure où chaque test d’une suite `SimpleTestCase` n’est pas lancé dans une transaction. Si ce problème ne vous concerne pas, vous pouvez désactiver ce comportement en définissant l’attribut de classe `databases` à `'__all__'` sur la classe de test.

Avertissement

`SimpleTestCase` et ses sous-classes (par ex. `TestCase`, …) se basent sur `setUpClass()` et `tearDownClass()` pour effectuer des initialisations liées à la classe entière (par exemple la surcharge de réglages). Si vous avez besoin de surcharger ces méthodes, n’oubliez pas d’appeler l’implémentation de `super`:

    class MyTestCase(TestCase):
        @classmethod
        def setUpClass(cls):
            super().setUpClass()
            ...

        @classmethod
        def tearDownClass(cls):
            ...
            super().tearDownClass()

Prenez garde de prendre en compte le comportement Python quand une exception est générée durant `setUpClass()`. Quand cela se produit, aucun test de la classe n’est exécuté et `tearDownClass()` n’est pas non plus appelée. Dans le cas de `django.test.TestCase`, la transaction créée dans `super()` n’est pas proprement traitée ce qui peut produire divers symptômes, y compris une faute de segmentation sur certaines plates-formes (signalé sur macOS). Si vous souhaitez provoquer intentionnellement une exception telle que `unittest.SkipTest` dans `setUpClass()`, faites-le avant d’appeler `super()` pour éviter ces problèmes.

### `TransactionTestCase`

class TransactionTestCase

`TransactionTestCase` hérite de `SimpleTestCase` et y ajoute quelques fonctionnalités spécifiques aux bases de données :

- Réinitialisation de la base de données à un état connu à la fin de chaque test pour faciliter les tests et l’utilisation de l’ORM.

- Les instantanés (`fixtures`) de base de données.

- Omission de tests en fonction de fonctionnalités du moteur de base de données.

- Les méthodes spécialisées restantes du type `assert*`.

Django’s `TestCase` class is a more commonly used subclass of `TransactionTestCase` that makes use of database transaction facilities to speed up the process of resetting the database to a known state at the end of each test. A consequence of this, however, is that some database behaviors cannot be tested within a Django `TestCase` class. For instance, you cannot test that a block of code is executing within a transaction, as is required when using `select_for_update()`. In those cases, you should use `TransactionTestCase`.

`TransactionTestCase` et `TestCase` sont identiques à l’exception de la manière dont la base de données est réinitialisée à un état connu et de la capacité de tester les effets de « commit » et de « rollback » dans le code :

- La classe `TransactionTestCase` réinitialise la base de données après le lancement des tests en tronquant toutes les tables. Le code de ces tests peut valider et annuler des transactions et observer les effets de ces appels sur la base de données.

- D’un autre côté, un `TestCase` ne tronque pas les tables après un test. Il enveloppe plutôt le code de test dans une transaction de base de données qui est ensuite annulée lorsque le test est terminé. Cela permet de garantir que l’annulation (« rollback ») à la fin des tests restaure la base de données à son état initial.

Avertissement

Les tests `TestCase` s’exécutant avec une base de données ne prenant pas en charge l’annulation (« rollback »), par exemple MySQL avec le moteur MyISAM, ainsi que toutes les instances de tests `TransactionTestCase` annulent la transaction à la fin du test en supprimant toutes les données de la base de données de test.

Les applications ne verront pas leurs données rechargées. Si vous avez besoin de cette fonctionnalité (typiquement dans le cas des applications tierces), vous pouvez définir `serialized_rollback = True` dans le corps de la classe `TestCase`.

### `TestCase`

class TestCase

Il s’agit de la classe la plus courante pour l’écriture de tests dans Django. Elle hérite de `TransactionTestCase` (et par extension de `SimpleTestCase`). Si votre application Django n’utilise pas de base de données, utilisez `SimpleTestCase`.

La classe :

- Enveloppe les tests dans deux blocs `atomic()` imbriqués : un pour la classe entière et un pour chaque test. Ainsi, si vous souhaitez tester un comportement transactionnel précis de la base de données, utilisez `TransactionTestCase`.

- Vérifie les contraintes de base de données différées à la fin de chaque test.

Elle fournit également une méthode supplémentaire :

classmethod TestCase.setUpTestData()

  Le bloc `atomic` au niveau de la classe mentionné ci-dessus permet la création de données initiales au niveau de la classe, une seule fois pour l’ensemble de `TestCase`. Cette technique permet d’accélérer les tests en comparaison du travail équivalent effectué au niveau de `setUp()`.

  Par exemple :

      from django.test import TestCase

      class MyTests(TestCase):
          @classmethod
          def setUpTestData(cls):
              # Set up data for the whole TestCase
              cls.foo = Foo.objects.create(bar="Test")
              ...

          def test1(self):
              # Some test using self.foo
              ...

          def test2(self):
              # Some other test using self.foo
              ...

  Notez que si les tests sont exécutés pour une base de données sans prise en charge des transactions (par exemple MySQL avec le moteur MyISAM), `setUpTestData()` sera appelée avant chaque test, ce qui annulera le bénéfice en terme de vitesse.

  Objects assigned to class attributes in `setUpTestData()` must support creating deep copies with `copy.deepcopy()` in order to isolate them from alterations performed by each test methods.

classmethod TestCase.captureOnCommitCallbacks(using=DEFAULT_DB_ALIAS, execute=False)

  Renvoie un gestionnaire de contexte capturant les fonctions de rappel `transaction.on_commit()` pour la connexion de base de données indiquée. À la sortie du contexte, il renvoie une liste contenant les fonctions de rappel capturées. À partir de cette liste, vous pouvez placer des assertions sur ces fonctions ou les appeler pour produire leurs effets de bord, simulant un commit.

  `using` est l’alias de la connexion de base de données pour laquelle les fonctions de rappel seront capturées.

  Si `execute` vaut `True`, toutes les fonctions de rappel seront exécutées au moment de la sortie du gestionnaire de contexte, dans le cas ou aucune exception n’a été produite. Ceci simule un commit après le bloc de code enveloppé.

  Par exemple :

      from django.core import mail
      from django.test import TestCase

      class ContactTests(TestCase):
          def test_post(self):
              with self.captureOnCommitCallbacks(execute=True) as callbacks:
                  response = self.client.post(
                      "/contact/",
                      {"message": "I like your site"},
                  )

              self.assertEqual(response.status_code, 200)
              self.assertEqual(len(callbacks), 1)
              self.assertEqual(len(mail.outbox), 1)
              self.assertEqual(mail.outbox[0].subject, "Contact Form")
              self.assertEqual(mail.outbox[0].body, "I like your site")

### `LiveServerTestCase`

class LiveServerTestCase

`LiveServerTestCase` est à la base identique à `TransactionTestCase` avec une fonction supplémentaire : elle lance un serveur Django en arrière-plan lors de la préparation (« setUp ») des tests et l’arrête lors du nettoyage (« tearDown »). Cela permet l’utilisation de clients de tests automatisés autres que le client élémentaire de Django, comme par exemple le client Selenium, afin d’exécuter une série de tests fonctionnels dans un navigateur et de simuler ainsi des actions d’un utilisateur réel.

Le serveur « live » écoute sur `localhost` et se lie au port 0, ce qui utilise un port libre attribué par le système d’exploitation. L’URL du serveur est accessible dans `self.live_server_url` pendant les tests.

Pour démontrer comment utiliser `LiveServerTestCase`, écrivons un test Selenium. Premièrement, il s’agit d’installer le paquet selenium :

/ 

    $ python -m pip install "selenium >= 4.23.0"

    ...\> py -m pip install "selenium >= 4.23.0"

Puis, ajoutez un test basé sur `LiveServerTestCase` au module tests de votre application (par exemple `myapp/tests.py`). Pour cet exemple, nous supposons que vous utilisez l’application `staticfiles` et que vous souhaitez disposez des fichiers statiques servis durant l’exécution des tests tout comme ce qu’on a en développement avec `DEBUG=True`, c’est-à-dire sans devoir les collecter avec `collectstatic`. Nous allons utiliser la sous-classe `StaticLiveServerTestCase` qui fournit cette fonctionnalité. Remplacez-la par `django.test.LiveServerTestCase` si vous n’en avez pas besoin.

Le code de ce test pourrait ressembler à ceci :

    from django.contrib.staticfiles.testing import StaticLiveServerTestCase
    from selenium.webdriver.common.by import By
    from selenium.webdriver.firefox.webdriver import WebDriver

    class MySeleniumTests(StaticLiveServerTestCase):
        fixtures = ["user-data.json"]

        @classmethod
        def setUpClass(cls):
            super().setUpClass()
            cls.selenium = WebDriver()
            cls.selenium.implicitly_wait(10)

        @classmethod
        def tearDownClass(cls):
            cls.selenium.quit()
            super().tearDownClass()

        def test_login(self):
            self.selenium.get(f"{self.live_server_url}/login/")
            username_input = self.selenium.find_element(By.NAME, "username")
            username_input.send_keys("myuser")
            password_input = self.selenium.find_element(By.NAME, "password")
            password_input.send_keys("secret")
            self.selenium.find_element(By.XPATH, '//input[@value="Log in"]').click()

Finalement, lancez le test comme ceci :

/ 

    $ ./manage.py test myapp.tests.MySeleniumTests.test_login

    ...\> manage.py test myapp.tests.MySeleniumTests.test_login

Cet exemple ouvre Firefox automatiquement puis se rend à la page de connexion, saisit les données d’authentification et appuie sur le bouton « Log in ». Selenium propose d’autres pilotes dans le cas où Firefox n’est pas installé ou que vous voudriez tester avec un autre navigateur. L’exemple ci-dessus n’est qu’une fraction de ce que le client Selenium est capable de faire ; consultez la référence complète pour plus de détails.

Note

Lorsque les tests utilisent une base de données SQLite en mémoire, une même connexion de base de données sera partagée par deux fils d’exécution en parallèle : le fil dans lequel tourne le serveur « live » et le fil utilisé pour faire fonctionner le cas de test. Il est important de prévenir les requêtes de base de données simultanées au travers de cette connexion partagée, car cela pourrait provoquer l’échec aléatoire de certains tests. Vous devez donc vous assurer que les deux fils d’exécution n’accèdent pas à la base de données au même moment. En particulier, cela signifie que dans certains cas (par exemple juste après avoir cliqué sur un lien ou soumis un formulaire), il est nécessaire de contrôler qu’une réponse a été reçue par Selenium et que la page suivante a été chargée avant de continuer avec la suite de l’exécution des tests. Cela peur par exemple se faire en faisant attendre Selenium jusqu’à ce que la balise HTML `<body>` soit présente dans la réponse (nécessite Selenium > 2.13) :

    def test_login(self):
        from selenium.webdriver.support.wait import WebDriverWait

        timeout = 2
        ...
        self.selenium.find_element(By.XPATH, '//input[@value="Log in"]').click()
        # Wait until the response is received
        WebDriverWait(self.selenium, timeout).until(
            lambda driver: driver.find_element(By.TAG_NAME, "body")
        )

L’élément délicat ici est que le concept de « chargement de page » n’existe pas, particulièrement dans les applications web modernes qui produisent du code HTML dynamique après la génération du document initial par le serveur. Ceci fait que le contrôle de la présence de `<body>` dans la réponse n’est pas forcément approprié dans tous les cas de figure. Consultez la FAQ Selenium ainsi que la documentation Selenium pour obtenir davantage d’informations.

## Fonctionnalités des cas de test

### Client de test par défaut

SimpleTestCase.client

Chaque cas de test dans une instance de `django.test.*TestCase` peut accéder à une instance du client de test de Django. Ce client est disponible dans self.client. Il est recréé pour chaque test, il n’y a donc pas besoin de se soucier de son état (comme les cookies) qui pourrait se propager d’un test à l’autre.

Cela signifie qu’au lieu de créer une instance de `Client` dans chaque test :

    import unittest
    from django.test import Client

    class SimpleTest(unittest.TestCase):
        def test_details(self):
            client = Client()
            response = client.get("/customer/details/")
            self.assertEqual(response.status_code, 200)

        def test_index(self):
            client = Client()
            response = client.get("/customer/index/")
            self.assertEqual(response.status_code, 200)

…vous pouvez faire appel à `self.client`, comme ceci :

    from django.test import TestCase

    class SimpleTest(TestCase):
        def test_details(self):
            response = self.client.get("/customer/details/")
            self.assertEqual(response.status_code, 200)

        def test_index(self):
            response = self.client.get("/customer/index/")
            self.assertEqual(response.status_code, 200)

### Personnalisation du client de test

SimpleTestCase.client_class

Si vous souhaitez utiliser une classe `Client` différente (par exemple une sous-classe avec un comportement adapté), utilisez l’attribut de classe `client_class`:

    from django.test import Client, TestCase

    class MyTestClient(Client):
        # Specialized methods for your environment
        ...

    class MyTest(TestCase):
        client_class = MyTestClient

        def test_my_stuff(self):
            # Here self.client is an instance of MyTestClient...
            call_some_test_code()

### Chargement des instantanés

TransactionTestCase.fixtures

Une classe de cas de test pour un site Web adossé à une base de données n’est pas très utile s’il n’y a pas de données en base de données. Les tests sont plus lisibles et la maintenabilité est meilleure si les objets sont créés avec l’ORM, par exemple dans `TestCase.setUpTestData()`. Cependant, vous pouvez aussi utiliser des instantanés.

Un instantané est une série de données que Django sait importer dans la base de données. Par exemple, si votre site contient des comptes utilisateurs, il peut être utile de créer un instantané de comptes utilisateurs afin de remplir la base de données pendant les tests.

La façon la plus directe de créer un instantané est d’utiliser la commande `manage.py dumpdata`, en partant du principe que le base de données contient déjà les données nécessaires. Consultez la `documentation de dumpdata` pour plus de détails.

Après avoir créé un instantané et l’avoir placé dans un répertoire `fixtures` dans l’une des applications de `INSTALLED_APPS`, vous pouvez l’utiliser dans vos tests unitaires en définissant un attribut de classe `fixtures` dans votre sous-classe de `django.test.TestCase`:

    from django.test import TestCase
    from myapp.models import Animal

    class AnimalTestCase(TestCase):
        fixtures = ["mammals.json", "birds"]

        def setUp(self):
            # Test definitions as before.
            call_setup_methods()

        def test_fluffy_animals(self):
            # A test that uses the fixtures.
            call_some_test_code()

Voici ce qui se passe en pratique :

- Pendant `setUpClass()`, tous les instantanés nommés sont installés. Dans cet exemple, Django installe tout instantané JSON nommé `mammals`, suivi par tout instantané nommé `birds`. Consultez la thématique Instantanés pour plus de détails sur la définition et l’installation d’instantanés.

Pour la plupart des tests unitaires utilisant `TestCase`, Django n’a pas besoin de faire autre chose, car les transactions sont utilisées pour nettoyer la base de données après chaque test pour des raisons de performances. Mais pour `TransactionTestCase`, les actions suivantes vont se dérouler :

- À la fin de chaque test, Django réinitialise la base de données telle qu’elle se trouvait juste après avoir exécuté `migrate`.

- Pour chaque test suivant, les instantanés seront rechargés avant que `setUp()` soit exécuté.

Dans tous les cas, vous pouvez ainsi être sûr que le résultat d’un test ne sera pas affecté par un autre test ou par l’ordre d’exécution des tests.

Par défaut, les instantanés ne sont chargés que dans la base de données `default`. Si vous utilisez plusieurs bases de données et que vous définissez :`TransactionTestCase.databases`, les instantanés seront chargés dans toutes les bases de données indiquées.

Changed in Django 5.2:

Pour `TransactionTestCase`, les instantanés sont rendus disponibles pendant `setUpClass()`.

### Configuration des URL

Si une application contient des vues, il peut être souhaitable d’inclure des tests où le client de test éprouve ces vues. Cependant, un utilisateur de l’application est libre de déployer les vues d’une application à l’URL de son choix. Cela signifie que les tests ne peuvent pas compter sur des URL figées pour accéder aux vues. Décorez vos classes ou méthodes de test avec `@override_settings(ROOT_URLCONF=...)` pour imposer des configurations d’URL.

### Prise en charge de plusieurs bases de données

TransactionTestCase.databases

Django met en place une base de données de test pour chaque base de données définie dans la définition de `DATABASES` dans vos réglages et qui est référencée par au moins un test au travers de `databases`.

Cependant, une bonne partie du temps nécessaire à exécuter un cas de test Django est passé dans l’appel à `flush` (réinitialisation des données) permettant de retrouver une base de données propre à la fin de chaque test. Pour un projet avec plusieurs bases de données, plusieurs commandes `flush` sont nécessaires (une par base de données), ce qui peut représenter un temps non négligeable, particulièrement si les tests n’ont pas pour but de tester l’activité entre plusieurs bases de données.

Dans une optique d’optimisation, Django ne réinitialise que la base de données `default` à la fin de chaque test. Si votre configuration contient plusieurs bases de données et que certains tests nécessitent que toutes les bases de données soient propres, vous pouvez définir l’attribut `databases` de la suite de tests pour provoquer la réinitialisation des bases de données supplémentaires.

Par exemple :

    class TestMyViews(TransactionTestCase):
        databases = {"default", "other"}

        def test_index_page_view(self):
            call_some_test_code()

Cette classe de cas de test réinitialise les bases de données de test `default` et `other` après avoir exécuté `test_index_page_view`. Vous pouvez aussi indiquer `'__all__'` qui demande à ce que toutes les bases de données de test soient réinitialisées.

L’option `databases` contrôle également les bases données dans lesquelles sont chargés les instantanés (`TransactionTestCase.fixtures`). Par défaut, les instantanés ne sont chargés que dans la base de données `default`.

Les requêtes ciblant des bases de données absentes de `databases` produiront des erreurs d’assertion pour éviter des fuites d’état entre les tests.

TestCase.databases

Par défaut, seule la base de données `default` est enveloppée dans une transaction durant l’exécution d’un cas de test ; toute tentative d’interroger une autre base de données produira une erreur d’assertion pour empêcher toute fuite d’état entre les tests.

Utilisez l’attribut de classe `databases` sur la classe de test pour demander l’emploi des transactions pour les bases de données autres que `default`.

Par exemple :

    class OtherDBTests(TestCase):
        databases = {"other"}

        def test_other_db_query(self): ...

Ce test n’autorisera que les requêtes vers las base de données `other`. Comme pour `SimpleTestCase.databases` et `TransactionTestCase.databases`, la constante `'__all__'` peut être employée pour indiquer que le test doit autoriser les requêtes vers toutes les bases de données.

### Surcharge des réglages

Avertissement

Utilisez les fonctions ci-dessous pour modifier temporairement la valeur de certains réglages pendant les tests. Ne manipulez pas directement `django.conf.settings` car Django ne s’occupe pas de restaurer les valeurs d’origine après de telles manipulations.

SimpleTestCase.settings()

À des fins de tests, il est souvent utile de modifier temporairement un réglage puis de retrouver la valeur d’origine après l’exécution du code des tests. Pour cette situation, Django offre un gestionnaire de contexte de style Python (voir PEP 343) nommé `settings()` qui peut être utilisé comme ceci :

    from django.test import TestCase

    class LoginTestCase(TestCase):
        def test_login(self):
            # First check for the default behavior
            response = self.client.get("/sekrit/")
            self.assertRedirects(response, "/accounts/login/?next=/sekrit/")

            # Then override the LOGIN_URL setting
            with self.settings(LOGIN_URL="/other/login/"):
                response = self.client.get("/sekrit/")
                self.assertRedirects(response, "/other/login/?next=/sekrit/")

Cet exemple surcharge le réglage `LOGIN_URL` pour le code contenu dans le bloc `with` et réapplique la valeur originale à la fin du bloc.

SimpleTestCase.modify_settings()

La redéfinition de réglages contenant une liste de valeurs peut se révéler ardue. En pratique, l’ajout ou la suppression de valeurs est souvent suffisante. Django fournit le gestionnaire de contexte `modify_settings()` pour changer plus facilement les réglages :

    from django.test import TestCase

    class MiddlewareTestCase(TestCase):
        def test_cache_middleware(self):
            with self.modify_settings(
                MIDDLEWARE={
                    "append": "django.middleware.cache.FetchFromCacheMiddleware",
                    "prepend": "django.middleware.cache.UpdateCacheMiddleware",
                    "remove": [
                        "django.contrib.sessions.middleware.SessionMiddleware",
                        "django.contrib.auth.middleware.AuthenticationMiddleware",
                        "django.contrib.messages.middleware.MessageMiddleware",
                    ],
                }
            ):
                response = self.client.get("/")
                # ...

Pour chaque action, vous pouvez indiquer soit une liste de valeurs, soit une chaîne. Lorsque la valeur existe déjà dans la liste, `append` et `prepend` n’ont pas d’effet ; de même que `remove` lorsque la valeur n’existe pas.

override_settings(**kwargs)

Dans le cas où vous souhaitez remplacer un réglage pour une méthode de test, Django met à disposition le décorateur `override_settings()` (voir PEP 318). Voici comment l’utiliser :

    from django.test import TestCase, override_settings

    class LoginTestCase(TestCase):
        @override_settings(LOGIN_URL="/other/login/")
        def test_login(self):
            response = self.client.get("/sekrit/")
            self.assertRedirects(response, "/other/login/?next=/sekrit/")

Le décorateur peut aussi être appliqué à des classes `TestCase`:

    from django.test import TestCase, override_settings

    @override_settings(LOGIN_URL="/other/login/")
    class LoginTestCase(TestCase):
        def test_login(self):
            response = self.client.get("/sekrit/")
            self.assertRedirects(response, "/other/login/?next=/sekrit/")

modify_settings(*args, **kwargs)

Dans le même ordre d’idée, Django fournit le décorateur `modify_settings()`:

    from django.test import TestCase, modify_settings

    class MiddlewareTestCase(TestCase):
        @modify_settings(
            MIDDLEWARE={
                "append": "django.middleware.cache.FetchFromCacheMiddleware",
                "prepend": "django.middleware.cache.UpdateCacheMiddleware",
            }
        )
        def test_cache_middleware(self):
            response = self.client.get("/")
            # ...

Le décorateur peut aussi être appliqué à des classes de cas de test :

    from django.test import TestCase, modify_settings

    @modify_settings(
        MIDDLEWARE={
            "append": "django.middleware.cache.FetchFromCacheMiddleware",
            "prepend": "django.middleware.cache.UpdateCacheMiddleware",
        }
    )
    class MiddlewareTestCase(TestCase):
        def test_cache_middleware(self):
            response = self.client.get("/")
            # ...

Note

Lorsqu’ils reçoivent une classe, ces décorateurs modifient directement la classe et la renvoie ; la classe renvoyée n’est pas une copie. Ainsi si vous essayez de manipuler les exemples ci-dessus pour que la valeur renvoyée soit nommée différemment que `LoginTestCase` ou `MiddlewareTestCase`, vous pourriez être surpris de constater que les classes de cas de test originales sont tout de même affectées par le décorateur. Pour une classe donnée, `modify_settings()` est toujours appliqué après `override_settings()`.

Avertissement

Le fichier de réglages contient certains réglages qui ne sont consultés que lors de l’initialisation de paramètres internes à Django. Si vous les modifier avec `override_settings`, ces réglages sont bien modifiés si vous les appelez depuis le module `django.conf.settings`, mais les éléments internes de Django accèdent différemment à ces réglages. En pratique, l’utilisation de `override_settings()` ou de `modify_settings()` avec ces réglages ne va probablement pas avoir l’effet que vous attendez.

Il n’est pas recommandé de modifier le réglage `DATABASES`. La modification du réglage `CACHES` est possible, mais un peu délicat si vous utilisez des éléments internes qui s’appuient sur du cache, comme `django.contrib.sessions`. Par exemple, il faut réinitialiser le moteur de sessions dans un test qui utilise les sessions en cache et qui surcharge `CACHES`.

Pour terminer, évitez de créer des alias de réglages comme constantes de niveau module, car `override_settings()` ne fonctionnera pas avec de telles valeurs qui ne sont évaluées que lors de la première importation du module.

Il est aussi possible de simuler l’absence d’un réglage en supprimant celui-ci après que les réglages aient été surchargés, comme ceci :

    @override_settings()
    def test_something(self):
        del settings.LOGIN_URL
        ...

Lors de la surcharge de réglages, prenez soin de gérer les cas où le code de votre application utilise du cache ou un mécanisme similaire conservant l’état même quand le réglage a changé. Django fournit le signal `django.test.signals.setting_changed` permettant d’inscrire des fonctions de rappel qui serviront à réinitialiser l’état lorsque les réglages ont changé.

Django utilise lui-même ce signal pour réinitialiser différentes données :

Réglages surchargés | Données réinitialisées
--- | ---
USE_TZ, TIME_ZONE | Fuseau horaire de la base de données
TEMPLATES | Moteurs de gabarits
FORM_RENDERER | Moteur de rendu par défaut
SERIALIZATION_MODULES | Cache des sérialiseurs
LOCALE_PATHS, LANGUAGE_CODE | Traduction par défaut et traductions chargées
STATIC_ROOT, STATIC_URL, STORAGES | Configuration des stockages

### Isolation des applications

utils.isolate_apps(*app_labels, attr_name=None, kwarg_name=None)

  Inscrit les modèles définis dans un contexte enveloppeur dans leur propre registre `apps` isolé. Cette fonctionnalité est utile lors de la création de classes de modèles pour les tests, car ainsi ces classes seront proprement effacées à la fin, et cela évite des risques de collisions de noms.

  Les étiquettes d’application que le registre isolé doit contenir doivent être transmises sous forme d’arguments individuels. Vous pouvez utiliser `isolate_apps()` comme un décorateur ou un gestionnaire de contexte. Par exemple

      from django.db import models
      from django.test import SimpleTestCase
      from django.test.utils import isolate_apps

      class MyModelTests(SimpleTestCase):
          @isolate_apps("app_label")
          def test_model_definition(self):
              class TestModel(models.Model):
                  pass

              ...

  …ou :

      with isolate_apps("app_label"):

          class TestModel(models.Model):
              pass

          ...

  La forme décorateur peut aussi être appliquée à des classes.

  Deux paramètres nommés facultatifs peuvent être indiqués :

  - `attr_name`: attribut recevant le registre isolé si la forme décorateur de classe est utilisée.

  - `kwarg_name`: paramètre nommé utilisé pour transmettre le registre isolé si la forme décorateur de fonction est utilisée.

  L’instance temporaire `Apps` utilisée pour isoler l’inscription du modèle peut être récupérée comme un attribut lorsqu’utilisée en tant que décorateur de classe au moyen du paramètre `attr_name`:

      @isolate_apps("app_label", attr_name="apps")
      class TestModelDefinition(SimpleTestCase):
          def test_model_definition(self):
              class TestModel(models.Model):
                  pass

              self.assertIs(self.apps.get_model("app_label", "TestModel"), TestModel)

  …ou en tant que paramètre de la méthode de test lorsqu’utilisée comme décoratrice de méthode au moyen du paramètre `kwarg_name`:

      class TestModelDefinition(SimpleTestCase):
          @isolate_apps("app_label", kwarg_name="apps")
          def test_model_definition(self, apps):
              class TestModel(models.Model):
                  pass

              self.assertIs(apps.get_model("app_label", "TestModel"), TestModel)

### Vidage de la boîte de messagerie de test

Si vous utilisez l’une des classes `TestCase` personnalisées de Django, l’exécuteur de tests efface le contenu de la boîte de messagerie de test au début de chaque cas de test.

Pour plus de détails sur les services de messagerie durant les tests, consultez Services de messagerie plus bas dans ce document.

### Assertions

De la même manière que la classe `unittest.TestCase` normale de Python implément des méthodes d’assertion telles que `assertTrue()` et `assertEqual()`, la classe `TestCase` propre à Django fournit un certain nombre de méthodes d’assertion utiles pour tester les applications web :

Les messages d’échec produits par la plupart de ces méthodes d’assertion peuvent être personnalisés par le paramètre `msg_prefix`. Cette chaîne préfixe tout message d’échec généré par l’assertion. Ceci permet d’ajouter des détails pouvant aider à identifier l’emplacement et la cause d’un échec dans une suite de tests.

SimpleTestCase.assertRaisesMessage(expected_exception, expected_message, callable, *args, **kwargs)
SimpleTestCase.assertRaisesMessage(expected_exception, expected_message)

  Confirme que l’appel à l’exécutable `callable` génère l’exception `expected_exception` et que `expected_message` est trouvé dans le message de l’exception. Tout autre résultat est signalé comme un échec. Il s’agit d’une version simplifiée de `unittest.TestCase.assertRaisesRegex()`, à la différence près que `expected_message` n’est ici pas traité comme une expression régulière.

  Si seuls les paramètres `expected_exception` et `expected_message` sont donnés, renvoie un gestionnaire de contexte afin que le code en cours de test puisse être écrit en ligne plutôt que sous la forme d’une fonction :

      with self.assertRaisesMessage(ValueError, "invalid literal for int()"):
          int("a")

SimpleTestCase.assertWarnsMessage(expected_warning, expected_message, callable, *args, **kwargs)
SimpleTestCase.assertWarnsMessage(expected_warning, expected_message)

  Analogue à `SimpleTestCase.assertRaisesMessage()` mais pour `assertWarnsRegex()` au lieu de `assertRaisesRegex()`.

SimpleTestCase.assertFieldOutput(fieldclass, valid, invalid, field_args=None, field_kwargs=None, empty_value='')

  Confirme qu’un champ de formulaire se comporte correctement avec différentes valeurs soumises.

  Paramètres:

    - fieldclass – la classe du champ à tester.

    - valid – un dictionnaire faisant correspondre des valeurs de saisies valides aux valeurs nettoyées attendues.

    - invalid – un dictionnaire faisant correspondre des valeurs de saisies non valides à un ou plusieurs messages d’erreur attendus.

    - field_args – les paramètres positionnels transmis pour la création du champ.

    - field_kwargs – les paramètres nommés transmis pour la création du champ.

    - empty_value – le résultat nettoyé attendu pour les valeurs saisies contenues dans `empty_values`.

  Par exemple, le code suivant teste qu’un champ `EmailField` accepte `a@a.com` comme adresse électronique valide, mais rejette `aaa` avec un message d’erreur adéquat :

      self.assertFieldOutput(
          EmailField, {"a@a.com": "a@a.com"}, {"aaa": ["Enter a valid email address."]}
      )

SimpleTestCase.assertFormError(form, field, errors, msg_prefix='')

  Confirme qu’un champ de formulaire génère la liste d’erreurs fournie.

  `form` est une instance de `Form`. Le formulaire doit être lié à des données (« bound ») mais pas forcément validé (`assertFormError()` va automatiquement appeler la méthode `full_clean()` du formulaire).

  `field` est le nom du champ de formulaire à contrôler. Pour contrôler les `erreurs non liées aux champs`, indiquez `field=None`.

  `errors` est une liste de toutes les chaînes d’erreurs que le champ peut potentiellement produire. Vous pouvez aussi transmettre une seule chaîne d’erreur si vous en attendez une seule, ce qui signifie que `errors='message d’erreur'` est identique à `errors=['message d’erreur']`.

SimpleTestCase.assertFormSetError(formset, form_index, field, errors, msg_prefix='')

  Confirme que `formset` génère la liste d’erreurs fournie lorsqu’il est affiché.

  `formset` est une instance de `FormSet`. Le jeu de formulaires doit être lié à des données (« bound ») mais pas forcément validé (`assertFormSetError()` va automatiquement appeler la méthode `full_clean()` du jeu de formulaires).

  `form_index` est le numéro du formulaire dans le jeu `FormSet` (commençant par 0). Indiquez `form_index=None` pour vérifier les erreurs non liées aux formulaires, c’est-à-dire les erreurs obtenues en appelant `formset.non_form_errors()`. Dans ce cas, vous devez aussi indiquer `field=None`.

  `field` et `errors` ont la même signification que les mêmes paramètres de `assertFormError()`.

SimpleTestCase.assertContains(response, text, count=None, status_code=200, msg_prefix='', html=False)

  Confirme qu’une `réponse` produit le code `status_code` indiqué et que le contenu `text` apparaît dans son contenu `content`. Si `count` est renseigné, `text` doit apparaître exactement `count` fois dans la réponse.

  Définissez `html` à `True` pour que `text` soit géré comme du code HTML. La comparaison avec le contenu de la réponse prend alors en compte la sémantique HTML au lieu d’une comparaison caractère par caractère. Les espaces blancs sont majoritairement ignorés et l’ordre des attributs ne joue pas de rôle. Voir `assertHTMLEqual()` pour plus de détails.

SimpleTestCase.assertNotContains(response, text, status_code=200, msg_prefix='', html=False)

  Confirme qu’une `réponse` produit le code `status_code` indiqué et que le contenu `text` n’apparaît pas dans son contenu `content`.

  Définissez `html` à `True` pour que `text` soit géré comme du code HTML. La comparaison avec le contenu de la réponse prend alors en compte la sémantique HTML au lieu d’une comparaison caractère par caractère. Les espaces blancs sont majoritairement ignorés et l’ordre des attributs ne joue pas de rôle. Voir `assertHTMLEqual()` pour plus de détails.

SimpleTestCase.assertTemplateUsed(response, template_name, msg_prefix='', count=None)

  Confirme que le gabarit du nom indiqué a été utilisé pour produire la réponse.

  `response` doit être une instance de réponse renvoyée par le `client de test`.

  `template_name` doit être une chaîne du genre `'admin/index.html'`.

  Le paramètre `count` est un nombre entier indiquant le nombre de fois qu’un gabarit doit être produit. La valeur par défaut est `None`, ce qui signifie que le gabarit doit être produit une ou plusieurs fois.

  Vous pouvez l’utiliser comme un gestionnaire de contexte, comme ceci :

      with self.assertTemplateUsed("index.html"):
          render_to_string("index.html")
      with self.assertTemplateUsed(template_name="index.html"):
          render_to_string("index.html")

SimpleTestCase.assertTemplateNotUsed(response, template_name, msg_prefix='')

  Confirme que le gabarit du nom indiqué n’a pas été utilisé pour produire la réponse.

  Vous pouvez l’utiliser comme un gestionnaire de contexte de la même façon qu’avec `assertTemplateUsed()`.

SimpleTestCase.assertURLEqual(url1, url2, msg_prefix='')

  Confirme que deux URL sont identiques, en ignorant l’ordre des paramètres de requête à l’exception des paramètres de même nom. Par exemple, `/chemin/?x=1&y=2` est égal à `/chemin/?y=2&x=1`, mais `/chemin/?a=1&a=2` n’est pas égal à `/chemin/?a=2&a=1`.

SimpleTestCase.assertRedirects(response, expected_url, status_code=302, target_status_code=200, msg_prefix='', fetch_redirect_response=True)

  Confirme que la `réponse` renvoie un statut de redirection `status_code`, qu’elle redirige vers `expected_url` (y compris avec d’éventuelles données `GET`) et que la page finale a renvoyé le code `target_status_code`.

  Si la requête a utilisé le paramètre `follow`, les valeurs de `expected_url` et de `target_status_code` doivent être celles de la page finale de la chaîne de redirection.

  Si `fetch_redirect_response` vaut `False`, la page finale n’est pas chargée. Comme le client de test ne peut pas récupérer des URL externes, c’est particulièrement utile quand `expected_url` ne fait pas partie de votre application Django.

  Le protocole est géré correctement lors de comparaisons entre deux URL. Si aucun protocole n’est indiqué à l’emplacement de redirection, le protocole de la requête d’origine est utilisé. S’il est présent, le protocole dans `expected_url` est celui qui sera utilisé pour effectuer des comparaisons.

SimpleTestCase.assertHTMLEqual(html1, html2, msg=None)

  Confirme que les chaînes `html1` et `html2` sont équivalentes. La comparaison prend en compte la sémantique HTML. La comparaison tient compte des éléments suivants :

  - Les blancs avant et après les balises HTML sont ignorés.

  - Tous les types de blancs (espaces, tabulateurs, etc.) sont équivalents.

  - Toutes les balises ouvertes sont fermées implicitement, par exemple quand une balise de niveau supérieur est fermée ou quand le document HTML est fini.

  - Les balises vides sont équivalentes à leur version auto-fermante.

  - L’ordre des attributs d’un élément HTML n’est pas signifiant.

  - Les attributs booléens sans paramètre (comme `checked`) sont équivalents aux attributs dont le nom et la valeur sont identiques (voir les exemples).

  - Le texte, les références de caractères et les références d’entité qui se réfèrent au même caractère sont équivalents.

  Les exemples suivants sont des tests valides et ne génèrent pas d’exception `AssertionError`:

      self.assertHTMLEqual(
          "<p>Hello <b>&#x27;world&#x27;!</p>",
          """<p>
              Hello   <b>&#39;world&#39;! </b>
          </p>""",
      )
      self.assertHTMLEqual(
          '<input type="checkbox" checked="checked" id="id_accept_terms" />',
          '<input id="id_accept_terms" type="checkbox" checked>',
      )

  `html1` et `html2` doivent contenir du code HTML. Une exception `AssertionError` est générée si l’une de ces deux valeurs ne peut pas être analysée comme du code HTML.

  L’affichage en cas d’erreur peut être personnalisé avec le paramètre `msg`.

SimpleTestCase.assertHTMLNotEqual(html1, html2, msg=None)

  Confirme que les chaînes `html1` et `html2` ne sont pas équivalentes. La comparaison prend en compte la sémantique HTML. Voir `assertHTMLEqual()` pour plus de détails.

  `html1` et `html2` doivent contenir du code HTML. Une exception `AssertionError` est générée si l’une de ces deux valeurs ne peut pas être analysée comme du code HTML.

  L’affichage en cas d’erreur peut être personnalisé avec le paramètre `msg`.

SimpleTestCase.assertXMLEqual(xml1, xml2, msg=None)

  Confirme que les chaînes `xml1` et `xml2` sont équivalentes. La comparaison prend en compte la sémantique XML. Sur le même principe que `assertHTMLEqual()`, la comparaison se fait sur le contenu analysé, ce qui fait que seules les différences sémantiques sont prises en compte, pas la syntaxe elle-même. Si le contenu XML de l’un des paramètres n’est pas valide, une exception `AssertionError` est toujours générée, même si les deux chaînes sont identiques.

  La déclaration XML, le type de document, les instructions de traitement et les commentaires sont ignorés. Seul l’élément racine (root) et ses descendants sont comparés.

  L’affichage en cas d’erreur peut être personnalisé avec le paramètre `msg`.

SimpleTestCase.assertXMLNotEqual(xml1, xml2, msg=None)

  Confirme que les chaînes `xml1` et `xml2` ne sont pas équivalentes. La comparaison prend en compte la sémantique xML. Voir `assertXMLEqual()` pour plus de détails.

  L’affichage en cas d’erreur peut être personnalisé avec le paramètre `msg`.

SimpleTestCase.assertInHTML(needle, haystack, count=None, msg_prefix='')

  Confirme que le fragment HTML `needle` est contenu une fois dans le contenu `haystack`.

  Si le paramètre nombre entier `count` est indiqué, un contrôle supplémentaire est effectué que le nombre d’occurrences de `needle` correspond à `count`.

  Dans la plupart des cas, les blancs sont ignorés et l’ordre des attributs n’est pas pris en compte. Voir `assertHTMLEqual()` pour plus de détails.

SimpleTestCase.assertNotInHTML(needle, haystack, msg_prefix='')

  Confirme que le fragment HTML `needle` n’est pas contenu dans le contenu `haystack`.

  Dans la plupart des cas, les blancs sont ignorés et l’ordre des attributs n’est pas pris en compte. Voir `assertHTMLEqual()` pour plus de détails.

SimpleTestCase.assertJSONEqual(raw, expected_data, msg=None)

  Confirme que les fragments JSON `raw` et `expected_data` sont égaux. Les règles habituelles de JSON concernant les blancs non significatifs s’appliquent, car le gros du travail est confié à la bibliothèque `json`.

  L’affichage en cas d’erreur peut être personnalisé avec le paramètre `msg`.

SimpleTestCase.assertJSONNotEqual(raw, expected_data, msg=None)

  Confirme que les fragments JSON `raw` et `expected_data` ne sont pas égaux. Voir `assertJSONEqual()` pour plus de détails.

  L’affichage en cas d’erreur peut être personnalisé avec le paramètre `msg`.

TransactionTestCase.assertQuerySetEqual(qs, values, transform=None, ordered=True, msg=None)

  Confirme que le jeu de requête `qs` correspond à une itération particulière de valeurs `values`.

  Si `transform` est indiqué, `values` est comparé à une liste produite en appliquant `transform` à chaque membre de `qs`.

  Par défaut, la comparaison dépend aussi de l’ordre de tri. Si `qs` ne comporte pas d’ordre de tri implicite, vous pouvez définir le paramètre `ordered` à `False`, ce qui provoquera une comparaison sur des objets `collections.Counter`. Si l’ordre est indéfini (si le paramètre `qs` n’est pas trié et que la comparaison se fait avec plus d’une valeur triée) une exception `ValueError` est générée.

  L’affichage en cas d’erreur peut être personnalisé avec le paramètre `msg`.

TransactionTestCase.assertNumQueries(num, func, *args, **kwargs)

  Confirme que lorsque `func` est appelée avec `*args` et `**kwargs`, `num` requêtes de base de données sont effectuées.

  Si une clé `"using"` est présente dans `kwargs`, cette valeur est utilisée comme alias de base de données pour laquelle le nombre de requêtes sera contrôlé

      self.assertNumQueries(7, my_function, using="non_default_db")

  Si vous souhaitez appeler une fonction avec un paramètre `using`, vous pouvez le faire en enveloppant l’appel dans une fonction `lambda` pour y ajouter un paramètre supplémentaire

      self.assertNumQueries(7, lambda: my_function(using=7))

  Vous pouvez également l’utiliser comme un gestionnaire de contexte :

      with self.assertNumQueries(2):
          Person.objects.create(name="Aaron")
          Person.objects.create(name="Daniel")

### Étiquetage des tests

Il est possible d’étiqueter les tests afin de pouvoir plus facilement lancer un sous-ensemble particulier. Par exemple, vous pourriez étiqueter les tests rapides ou lents :

    from django.test import tag

    class SampleTestCase(TestCase):
        @tag("fast")
        def test_fast(self): ...

        @tag("slow")
        def test_slow(self): ...

        @tag("slow", "core")
        def test_slow_but_core(self): ...

Il est aussi possible d’étiqueter une classe de cas de test :

    @tag("slow", "core")
    class SampleTestCase(TestCase): ...

Les sous-classes héritent des classes parentes, et les méthodes héritent des étiquettes de leur classe. Par exemple

    @tag("foo")
    class SampleTestCaseChild(SampleTestCase):
        @tag("bar")
        def test(self): ...

`SampleTestCaseChild.test` possédera les étiquettes `'slow'`, `'core'`, `'bar'` et `'foo'`.

Puis, vous pouvez choisir quels tests lancer. Par exemple, pour ne lancer que les tests rapides :

/ 

    $ ./manage.py test --tag=fast

    ...\> manage.py test --tag=fast

Ou pour lancer les tests rapides et les tests principaux (même s’ils sont lents) :

/ 

    $ ./manage.py test --tag=fast --tag=core

    ...\> manage.py test --tag=fast --tag=core

Il est aussi possible d’exclure des tests par leur étiquette. Pour lancer les tests principaux qui ne sont pas lents :

/ 

    $ ./manage.py test --tag=core --exclude-tag=slow

    ...\> manage.py test --tag=core --exclude-tag=slow

`test --exclude-tag` a la priorité sur `test --tag`, si donc un test possède deux étiquettes et que vous choisissez l’une d’elle tout en excluant l’autre, le test ne sera pas lancé.

## Test de code asynchrone

Si vous souhaitez simplement tester le résultat de vues asynchrones, le client de test standard les lance dans une boucle asynchrone dédiée sans travail supplémentaire de votre part.

Cependant, si vous souhaitez écrire des tests pleinement asynchrones pour un projet Django, vous devrez prendre en compte plusieurs choses.

Tout d’abord, vos tests doivent être des méthodes `async def` de la classe de test (afin de leur fournir un contexte asynchrone). Django détectera automatiquement tout test de type `async def` et les enveloppera afin qu’ils s’exécutent dans leur propre boucle événementielle.

Si vous testez depuis une fonction asynchrone, vous devez éaglement utiliser le client de test asynchrone. Il est disponible comme `django.test.AsyncClient` ou comme `self.async_client` dans chaque test.

class AsyncClient(enforce_csrf_checks=False, raise_request_exception=True, *, headers=None, query_params=None, **defaults)

`AsyncClient` possède les mêmes méthodes et signatures que le client de test synchrone (normal), avec les exceptions suivantes :

- Lors de l’initialisation, les paramètres mot-clé arbitraires dans `defaults` sont ajoutés directement dans la portée ASGI.

- Les en-têtes transmis dans le paramètre nommé `extra` ne doivent pas comporter de préfixe `HTTP_` tel que requis par le client synchrone (voir `Client.get()`). Par exemple, voici comment définir un en-tête HTTP `Accept`:

      >>> c = AsyncClient()
      >>> c.get("/customers/details/", {"name": "fred", "age": 7}, ACCEPT="application/json")

En utilisant `AsyncClient`, toute méthode effectuant une requête doit être appelée avec `await`:

    async def test_my_thing(self):
        response = await self.async_client.get("/some-url/")
        self.assertEqual(response.status_code, 200)

Le client asynchrone peut aussi appeler des vues synchrones ; il s’exécute au travers du chemin de requête asynchrone de Django, qui prend en charge les deux modes. Toute vue appelée par `AsyncClient` reçoit un objet `ASGIRequest` dans son attribut `request` au lieu de l’objet `WSGIRequest` créé par le client normal.

Avertissement

Si vous utilisez des décorateurs de tests, ils doivent être compatibles avec l’asynchrone pour fonctionner correctement. Les décorateurs intégrés de Django se comporteront correctement, mais ceux de tierces parties peuvent sembler ne pas s’exécuter (ils « enveloppent » la mauvaise partie du flux d’exécution au lieu de votre test).

Si vous avez besoin d’utiliser ces décorateurs, vous devez alors plutôt décorer vos méthodes de test avec `async_to_sync()` à l’intérieur de celles-ci :

    from asgiref.sync import async_to_sync
    from django.test import TestCase

    class MyTests(TestCase):
        @mock.patch(...)
        @async_to_sync
        async def test_my_thing(self): ...

## Services de messagerie

Si l’une de vos vues Django envoie des courriels via la fonctionnalité d’envoi de courriels de Django, vous ne souhaiterez probablement pas réellement envoyer un courriel lors de chaque test de cette vue. C’est pour cette raison que le lanceur de tests de Django redirige automatiquement tous les courriels qu’il envoie dans une boîte artificielle. Cela vous permet aussi de tester chaque aspect de l’envoi de courriels, du nombre de messages envoyés jusqu’au contenu de chaque message, sans jamais envoyer réellement les messages.

Le lanceur de tests fait cela en remplaçant de manière transparente le moteur de messagerie normal par un moteur de test (n’ayez crainte, cela n’a aucun effet sur l’expédition de courriels en dehors de Django, comme un éventuel serveur de messagerie tournant sur votre machine).

django.core.mail.outbox

During test running, each outgoing email is saved in `django.core.mail.outbox`. This is a list of all `EmailMessage` instances that have been sent. The `outbox` attribute is a special attribute that is created only when the `locmem` email backend is used. It doesn’t normally exist as part of the `django.core.mail` module and you can’t import it directly. The code below shows how to access this attribute correctly.

Voici un exemple de test qui examine la longueur et le contenu de `django.core.mail.outbox`:

    from django.core import mail
    from django.test import TestCase

    class EmailTest(TestCase):
        def test_send_email(self):
            # Send message.
            mail.send_mail(
                "Subject here",
                "Here is the message.",
                "from@example.com",
                ["to@example.com"],
                fail_silently=False,
            )

            # Test that one message has been sent.
            self.assertEqual(len(mail.outbox), 1)

            # Verify that the subject of the first message is correct.
            self.assertEqual(mail.outbox[0].subject, "Subject here")

Comme noté précédemment, la boîte de messagerie de test est vidée au début de chaque test des classes Django `*TestCase`. Pour vider la boîte manuellement, attribuez une liste vide à `mail.outbox`:

    from django.core import mail

    # Empty the test outbox
    mail.outbox = []

## Commandes d’administration

Les commandes d’administration peuvent être testées avec la fonction `call_command()`. La sortie peut être redirigée vers une instance de `StringIO`:

    from io import StringIO
    from django.core.management import call_command
    from django.test import TestCase

    class ClosepollTest(TestCase):
        def test_command_output(self):
            out = StringIO()
            call_command("closepoll", poll_ids=[1], stdout=out)
            self.assertIn('Successfully closed poll "1"', out.getvalue())

## Exclusion de tests

La bibliothèque unittest fournit les décorateurs `@skipIf` rz `@skipUnless` pour permettre d’exclure des tests si vous savez à l’avance que ces tests vont échouer dans certaines conditions.

Par exemple, si la réussite d’un test exige la présence d’une certaine bibliothèque, il est possible de décorer le cas de test avec `@skipIf`. Puis, le lanceur de tests signalera que le test n’a pas été exécuté ainsi que la raison, au lieu de laisser le test échouer ou d’ignorer totalement le test.

En plus de ces comportements d’exclusion de test, Django ajoute deux décorateurs de test supplémentaires. Au lieu de se baser sur une valeur booléenne générique, ces décorateurs contrôlent les capacités d’une base de données et excluent le test concerné si la base de données ne gère pas la capacité nommément indiquée.

Ces décorateurs utilisent un identifiant textuel pour désigner les capacités de base de données. Cet identifiant correspond à un attribut de la classe des capacités de connexion de base de données. Voir la classe django.db.backends.base.features.BaseDatabaseFeatures pour obtenir une liste complète des capacités de base de données pouvant être utilisées comme critères d’exclusion de tests.

skipIfDBFeature(*feature_name_strings)

Exclut le test ou la classe `TestCase` décorés si toutes les capacités de base de données indiquées sont prises en charge.

Par exemple, le test suivant ne sera pas exécuté si la base de données gère les transactions (par ex. il ne serait pas exécuté avec PostgreSQL, mais il le serait avec MySQL utilisant des tables MyISAM) :

    class MyTests(TestCase):
        @skipIfDBFeature("supports_transactions")
        def test_transaction_behavior(self):
            # ... conditional test code
            pass

skipUnlessDBFeature(*feature_name_strings)

Exclut le test ou la classe `TestCase` décorés si au moins une des capacités de base de données indiquées n’est pas prise en charge.

Par exemple, le test suivant ne sera exécuté que si la base de données gère les transactions (par ex. il serait exécuté avec PostgreSQL, mais il ne le serait pas avec MySQL utilisant des tables MyISAM)

    class MyTests(TestCase):
        @skipUnlessDBFeature("supports_transactions")
        def test_transaction_behavior(self):
            # ... conditional test code
            pass