Lors de la création d'une application Django, un fichier tests.py est automatiquement généré.
La plupart des développeurs se posent un jour cette question.

“Ce fichier… je peux le supprimer, non ?”
“Mais comment devrais-je l’utiliser ?”

En réalité, tests.py est le point d'entrée de test officiellement recommandé par Django et,
une fois que vous y avez pris un peu de goût, il devient une véritable grande assurance lors des refactorisations, des ajouts de fonctionnalités et des mises à niveau de versions. (Django Project)

Dans cet article, nous allons aborder :

  • À quoi sert le fichier tests.py

  • Comment le rédiger et l'exécuter

  • Dans quelles situations il est particulièrement utile

  • Exemples d'utilisation pratique

nous allons tout résumer en une seule fois.


1. Quel est le fichier tests.py ?



Django utilise un framework de test basé sur unittest, la bibliothèque standard Python.

  • Vous créez des classes de tests qui héritent de django.test.TestCase,

  • vous rédigez des méthodes commençant par test_,

  • et il les trouve et les exécute automatiquement avec la commande ./manage.py test.

Lorsque vous créez une application avec startapp, un fichier tests.py est créé par défaut :

  • Pour les petits projets → un seul tests.py suffit

  • Pour les plus grands projets → Django recommande également de diviser en un package tests/ (test_models.py, test_views.py, etc.).

C'est-à-dire, nous pouvons le considérer comme “le lieu de base pour les tests au niveau de l'application”.


2. Quand est-il utile ? (Pourquoi devrions-nous utiliser des tests ?)

Les tests Django brillent notamment dans les situations suivantes.

  1. Validation des modèles/logiques métier

    • Plus la logique de calcul, les modifications d'état et les méthodes personnalisées sont complexes,

    • plus vous pouvez être sûr d'effectuer des refactorisations sans souci après avoir créé un test.

  2. Tests de vue/URL/contrôle des droits

    • Vérifier si le code de réponse change selon l'état d'authentification

    • Vérifier si la redirection/gestion d’erreurs est adéquate en cas de saisie incorrecte

    • Vérifier si le contexte nécessaire au modèle est correctement passé

  3. Prévention des bugs de régression

    • Une fois le bug résolu, conservez-le dans un test pour éviter que le même problème ne se reproduise
  4. Assurer la stabilité lors des mises à niveau de version

    • Lorsque vous augmentez la version de Django ou d'une bibliothèque

    • vous pouvez exécuter ./manage.py test pour commencer.


3. Exemple de base de tests.py



3-1. Test de modèle

# myapp/tests.py
from django.test import TestCase
from .models import Article

class ArticleModelTests(TestCase):
    def setUp(self):
        self.article = Article.objects.create(
            title="Titre de test",
            content="Contenu",
            views=0,
        )

    def test_increase_views(self):
        # given
        self.assertEqual(self.article.views, 0)

        # when
        self.article.increase_views()

        # then
        self.assertEqual(self.article.views, 1)
  • TestCase est une classe conçue pour Django qui étend unittest.TestCase.

  • Chaque test est isolé au niveau de la transaction et, pour chaque test, la base de données est annulée pour éviter toute interférence.


4. Comment tester les vues/URL - Utiliser Client

Django fournit un client HTTP dédié aux tests django.test.Client.
Vous pouvez envoyer des requêtes GET/POST à toutes les URL sans démarrer le serveur réel.

4-1. Test d'une vue simple

# myapp/tests.py
from django.test import TestCase
from django.urls import reverse
from .models import Article

class ArticleViewTests(TestCase):
    def setUp(self):
        self.article = Article.objects.create(
            title="Article 1",
            content="Contenu",
            views=0,
        )

    def test_article_detail_page_returns_200(self):
        url = reverse("article_detail", args=[self.article.id])

        response = self.client.get(url)

        self.assertEqual(response.status_code, 200)
        self.assertContains(response, "Article 1")

Les points clés ici sont :

  • self.client : une instance de client de test fournie par défaut dans TestCase

  • reverse("article_detail", args=[...]) : recherche par nom d'URL au lieu de hardcoder l'URL

  • assertContains : vérifie si une chaîne spécifique est incluse dans le corps de la réponse

4-2. Test de connexion/vérification des droits

from django.contrib.auth import get_user_model

class ArticlePermissionTests(TestCase):
    def setUp(self):
        self.user = get_user_model().objects.create_user(
            username="tester",
            password="pass1234",
        )
        self.article = Article.objects.create(
            title="secret",
            content="Contenu secret",
            views=0,
        )

    def test_anonymous_user_redirected_to_login(self):
        url = reverse("article_edit", args=[self.article.id])

        response = self.client.get(url)

        self.assertEqual(response.status_code, 302)
        self.assertIn("/accounts/login", response["Location"])

    def test_logged_in_user_can_access_edit_page(self):
        self.client.login(username="tester", password="pass1234")
        url = reverse("article_edit", args=[self.article.id])

        response = self.client.get(url)

        self.assertEqual(response.status_code, 200)
  • Vérifiez si un utilisateur anonyme est redirigé

  • Vérifiez si un utilisateur connecté peut accéder normalement

Vous pouvez initialement détecter les bugs liés aux autorisations.


5. Quand et comment exécuter tests.py ?

5-1. Exécution de tous les tests

$ python manage.py test
  • Il cherchera automatiquement et exécutera les fichiers test*.py sous le répertoire actuel
    unittest.TestCase ou sous-classe de django.test.TestCase.

5-2. Exécution d'une application spécifique

$ python manage.py test myapp

5-3. Exécution d'un module/classe/méthode spécifique

# Module complet myapp.tests
$ python manage.py test myapp.tests

# Cas de test spécifique
$ python manage.py test myapp.tests.ArticleViewTests

# Méthode de test spécifique
$ python manage.py test myapp.tests.ArticleViewTests.test_article_detail_page_returns_200

Il est bon d'adopter l'habitude de tester rapidement uniquement les parties modifiées pendant le développement.


6. Étendre tests.py en un package tests

À mesure que le projet grandit, un seul tests.py devient insuffisant.
Django recommande également de le diviser en structure de package dans sa documentation. (Django Project)

Par exemple :

myapp/
    tests/
        __init__.py
        test_models.py
        test_views.py
        test_forms.py
        test_api.py

À l'intérieur de chaque fichier :

# myapp/tests/test_models.py
from django.test import TestCase
from myapp.models import Article

class ArticleModelTests(TestCase):
    ...

Il est possible de les organiser ainsi et l'exécution reste la même.

$ python manage.py test myapp   # tout
$ python manage.py test myapp.tests.test_models  # uniquement les tests de modèle

7. Quelques cas d'utilisation pratiques

7-1. Prévenir les régressions avec des tests de reproduction des bugs

  1. Un bug se produit en production

  2. Écrire un test de reproduction localement (il devrait échouer normalement)

  3. Modifier le code

  4. Vérifier que le test réussit

  5. Committez avec git à la fois le test et le code modifié

Cela empêchera des bugs similaires de réapparaître lors de futurs refactorisations.

7-2. Fixer le format de réponse API

Lors des collaborations avec le frontend, le format de la réponse API est très important.

class ArticleApiTests(TestCase):
    def setUp(self):
        self.article = Article.objects.create(
            title="Titre API",
            content="Contenu",
            views=0,
        )

    def test_article_detail_api_response(self):
        url = reverse("api:article-detail", args=[self.article.id])

        response = self.client.get(url)
        self.assertEqual(response.status_code, 200)

        data = response.json()
        self.assertEqual(data["id"], self.article.id)
        self.assertIn("title", data)
        self.assertIn("content", data)
  • Précisez quels champs doivent absolument être inclus dans la réponse

  • Changer ou supprimer des noms de champ par erreur entraîne une échec immédiat du test

7-3. Tester la logique de validation des formulaires

from .forms import ArticleForm

class ArticleFormTests(TestCase):
    def test_title_is_required(self):
        form = ArticleForm(data={"title": "", "content": "Contenu"})

        self.assertFalse(form.is_valid())
        self.assertIn("title", form.errors)

    def test_valid_form(self):
        form = ArticleForm(data={"title": "Titre", "content": "Contenu"})

        self.assertTrue(form.is_valid())
  • Vous pouvez conserver les règles de validation du formulaire sous forme de tests comme document.

Développeur Django écrivant tests py

8. Conclusion - Ne supprimez pas tests.py, essayez de bien l'utiliser une fois

Pour résumer :

Le fichier tests.py est “le filet de sécurité d'une application Django”.

  • Au début, cela peut sembler ennuyeux, mais

  • une fois que vous aurez goûté à un flux de développement basé sur des tests,

  • le stress lors des refactorisations, des ajouts de fonctionnalités et des mises à niveau de versions diminuera considérablement.

Dans les projets réels :

  1. Lorsque vous ajoutez une nouvelle fonctionnalité – ajoutez au moins 1 ou 2 tests en même temps

  2. Lorsque vous corrigez un bug – commencez toujours par écrire un test qui reproduit ce bug

  3. Lorsque vous commencez à augmenter le projet – refactorisez tests.py en un package tests/

Suivre ce modèle vous offrira une expérience de développement complètement différente de celle d'un “projet Django sans tests”.