Skip to main content

Une application réutilisable pour tester les vues django

Project description

🚀 Django AutoTest

Arrêtez de perdre du temps à écrire des tests répétitifs. Configurez, et c'est parti !

Django AutoTest est un framework de test automatisé qui génère vos tests Django à partir d'une simple configuration. Fini le copier-coller de code boilerplate pour tester chaque vue — définissez votre config, et laissez le framework faire le reste.

# Au lieu d'écrire ça pour CHAQUE vue...
class ProductListViewTest(TestCase):
    def setUp(self):
        self.product1 = Product.objects.create(name="Test", price=10)
        self.product2 = Product.objects.create(name="Test2", price=20)
    
    def test_get_request(self):
        response = self.client.get(reverse('product-list'))
        self.assertEqual(response.status_code, 200)
        self.assertIn('products', response.context)
    
    def test_post_not_allowed(self):
        response = self.client.post(reverse('product-list'))
        self.assertEqual(response.status_code, 405)
    # ... 50 lignes de plus ...

# Vous écrivez juste ça :
class ProductListViewTest(AutoTestCase):
    config = AutoTestCaseConfig(
        config={
            "view": ProductListView,
            "url_name": "product-list",
            "fixtures": [{"model": Product, "count": 5}],
            "method_allowed": {"GET": {"enabled": True}}
        }
    )

Résultat : Tous les tests sont générés automatiquement. Status codes, contexte, champs requis, validation, formulaires — tout est testé sans écrire une ligne de code de test.


📋 Table des matières


🎯 Pourquoi Django AutoTest ?

Le problème

Quand vous développez une application Django avec 20, 50 ou 100 vues, écrire des tests devient vite répétitif et chronophage :

  • ✍️ Répétition du même code pour chaque vue
  • 🔄 Copier-coller de fixtures et de setup
  • 🐛 Oublis fréquents de cas de test importants
  • ⏱️ Perte de temps sur du code boilerplate au lieu de tester la logique métier

La solution

Django AutoTest génère automatiquement vos tests à partir d'une configuration déclarative :

  • Gain de temps massif : 90% de code en moins à écrire
  • 🎯 Tests exhaustifs : Le framework teste automatiquement tous les cas standards
  • 🔧 Extensible : Ajoutez vos tests personnalisés quand nécessaire
  • 🌍 Multilingue : Messages d'erreur en français ou anglais
  • 🧪 Fixtures automatiques : Génération intelligente de données de test

✨ Fonctionnalités

✅ Ce que Django AutoTest fait AUJOURD'HUI

🎨 Génération automatique de tests

  • Tests pour ListView, DetailView, CreateView, UpdateView, DeleteView
  • Tests pour Function-Based Views (FBV)
  • Tests GET, POST, PUT, PATCH, DELETE, HEAD
  • Tests de status codes (200, 302, 404, etc.)
  • Tests de contexte et de champs de réponse

🏭 ModelFactory intelligent

  • Génération automatique de données de test valides
  • Support des ForeignKey, OneToOneField, ManyToMany
  • Gestion des champs uniques (email, username, etc.)
  • Gestion des contraintes d'intégrité avec retry automatique
  • Création d'utilisateurs Django pour l'authentification
  • Cache intelligent pour éviter les doublons

📝 Validation de formulaires

  • Test des champs requis (required_fields)
  • Test des champs uniques (unique_fields)
  • Test des types de données (type_fields)
  • Test des longueurs min/max (len_fields)
  • Test des champs facultatifs (optional_fields)
  • Test des champs interdits (exclude_fields)
  • Tests inverses (champs manquants → erreur attendue)

🔐 Authentification

  • Support des vues avec LoginRequiredMixin
  • Création automatique d'utilisateur de test
  • Login/logout automatique selon la config

🌍 Multilingue

  • Messages d'erreur en français ou anglais
  • Configuration simple : lang="fr" ou lang="en"

🧩 Tests personnalisés

  • Ajout facile de tests custom via custom_tests
  • Accès aux fixtures créées automatiquement
  • Combinaison avec les tests auto-générés

📊 Déduction intelligente

  • Analyse automatique des vues Django génériques
  • Extraction des champs du modèle
  • Activation automatique des méthodes HTTP appropriées
  • Status codes prédits selon le type de vue

📦 Installation

Prérequis

  • Python 3.10+
  • Django 4.0+

Installation depuis le dépôt

# Cloner le projet
git clone https://github.com/votre-username/django-autotest.git

# Installer les dépendances
cd django-autotest
pip install -r requirements.txt

# Intégrer dans votre projet Django
cp -r djangoautotest_app /path/to/your/project/

Ajout à INSTALLED_APPS

# settings.py
INSTALLED_APPS = [
    # ...
    'djangoautotest_app',
]

⚡ Utilisation rapide

Exemple 1 : Test d'une ListView

from djangoautotest_app.core.configs import AutoTestCaseConfig
from djangoautotest_app.core.testcase import AutoTestCase
from myapp.views import ProductListView

class ProductListViewTest(AutoTestCase):
    config = AutoTestCaseConfig(
        config={
            "view": ProductListView,
            "url_name": "product-list",
            "fixtures": [
                {
                    "model": Product,
                    "object_name": "products",
                    "count": 10
                }
            ],
            "method_allowed": {
                "GET": {
                    "enabled": True,
                    "response": {
                        "status_code": 200,
                        "expected_fields": ["products", "page_obj"]
                    }
                }
            }
        }
    )

Résultat : Test automatique de la requête GET, vérification du status 200, présence des champs dans le contexte, et 10 produits créés automatiquement.

Exemple 2 : Test d'un CreateView avec formulaire

class ProductCreateViewTest(AutoTestCase):
    config = AutoTestCaseConfig(
        lang="fr",  # Messages en français
        config={
            "view": ProductCreateView,
            "url_name": "product-create",
            "method_allowed": {
                "POST": {
                    "enabled": True,
                    "auth_required": True,  # Nécessite authentification
                    "inverse_test": True,   # Active le test avec champs manquants
                    "request": {
                        "required_fields": ["name", "price", "category"],
                        "unique_fields": ["name"],
                        "len_fields": [
                            {"field": "name", "min": 3, "max": 100}
                        ],
                        "type_fields": [
                            {"field": "price", "type": float}
                        ]
                    },
                    "response": {
                        "status_code": 302,          # Succès → redirection
                        "inverse_status_code": 400   # Échec → erreur
                    }
                }
            }
        }
    )

Résultat :

  • Test POST avec tous les champs → succès (302)
  • Test POST sans champs requis → échec (400)
  • Test de l'unicité du nom
  • Test des longueurs et types
  • Utilisateur créé et connecté automatiquement

📚 Exemples détaillés

CreateView avec tests personnalisés

class UserRegistrationTest(AutoTestCase):
    config = AutoTestCaseConfig(
        config={
            "view": UserRegistrationView,
            "url_name": "register",
            "method_allowed": {
                "POST": {
                    "enabled": True,
                    "request": {
                        "required_fields": ["username", "email", "password"],
                        "len_fields": [
                            {"field": "password", "min": 8}
                        ]
                    }
                }
            }
        },
        custom_tests=[
            # Test personnalisé : vérifier que les emails en double sont refusés
            lambda self: self._test_duplicate_email()
        ]
    )
    
    def _test_duplicate_email(self):
        """Test custom : l'email doit être unique."""
        # Créer un premier utilisateur
        data = {"username": "user1", "email": "test@test.com", "password": "12345678"}
        self.client.post(reverse('register'), data=data)
        
        # Tentative avec le même email
        data2 = {"username": "user2", "email": "test@test.com", "password": "87654321"}
        response = self.client.post(reverse('register'), data=data2)
        
        self.assertEqual(response.status_code, 400)
        self.assertIn('email', response.context['form'].errors)

UpdateView avec fixture

class ProductUpdateTest(AutoTestCase):
    config = AutoTestCaseConfig(
        config={
            "view": ProductUpdateView,
            "url_name": "product-update",
            "fixtures": [
                {
                    "model": Product,
                    "object_name": "product",
                    "count": 1,
                    "create_kwargs": {
                        "name": "Initial Product",
                        "price": 19.99
                    }
                }
            ],
            "method_allowed": {
                "GET": {
                    "enabled": True,
                    "response": {
                        "status_code": 200,
                        "expected_fields": ["form", "object"]
                    }
                },
                "POST": {
                    "enabled": True,
                    "request": {
                        "required_fields": ["name", "price"]
                    },
                    "response": {
                        "status_code": 302
                    }
                }
            }
        }
    )
    
    # Accès facile à la fixture créée
    def test_product_name_updated(self):
        """Test personnalisé : vérifier que le nom est bien modifié."""
        old_name = self.product_0.name
        
        data = {"name": "Updated Product", "price": 29.99}
        url = reverse('product-update', kwargs={'pk': self.product_0.pk})
        self.client.post(url, data=data)
        
        self.product_0.refresh_from_db()
        self.assertNotEqual(self.product_0.name, old_name)
        self.assertEqual(self.product_0.name, "Updated Product")

🏗️ Architecture

Django AutoTest se compose de 3 modules principaux :

1. ModelFactory (models.py)

Génère des données de test intelligentes et valides.

factory = ModelFactory(max_depth=5, create_m2m=True)

# Créer un produit avec toutes ses relations
product = factory.create(Product)
# → Crée automatiquement la catégorie, le fournisseur, etc.

# Créer un utilisateur
user = factory.create_user(username="testuser")

Fonctionnalités :

  • Génération de données fake réalistes (via Faker)
  • Support ForeignKey, OneToOne, ManyToMany
  • Gestion des champs uniques avec retry
  • Cache pour éviter les doublons
  • Gestion des contraintes d'intégrité

2. AutoTestCaseConfig (configs.py)

Configuration déclarative des tests.

config = AutoTestCaseConfig(
    lang="fr",
    test_name="mon_test",
    test_description="Description du test",
    config={
        "view": MyView,
        "url_name": "my-url",
        "fixtures": [...],
        "method_allowed": {
            "GET": {...},
            "POST": {...}
        }
    },
    custom_tests=[...]
)

Fonctionnalités :

  • Validation de la configuration
  • Déduction automatique depuis les vues génériques
  • Fusion intelligente de configs
  • Messages multilingues

3. AutoTestCase (testcase.py)

Génération dynamique de méthodes de test.

class MyViewTest(AutoTestCase):
    config = AutoTestCaseConfig(...)

Fonctionnalités :

  • Génération via __init_subclass__
  • Création automatique de méthodes test_*
  • Assertions complètes
  • Support JSON et TemplateResponse
  • Client authentifié ou non

⚙️ Configuration complète

Structure de configuration

{
    "view": MaVue,                    # Vue Django à tester
    "url_name": "mon-url",            # Nom de l'URL Django
    
    "fixtures": [                     # Données de test à créer
        {
            "model": MonModel,
            "object_name": "objects",
            "count": 5,
            "create_kwargs": {...},   # Valeurs imposées (facultatif)
            "factory_kwargs": {...},  # Config ModelFactory (facultatif)
            "store": True             # Stocker sur self (facultatif)
        }
    ],
    
    "method_allowed": {
        "GET": {
            "enabled": True,          # Activer le test GET
            "auth_required": False,   # Authentification requise
            
            "request": {
                "query_params": {},   # Paramètres ?page=1
                "headers": {}         # Headers HTTP
            },
            
            "response": {
                "status_code": 200,           # Code attendu
                "expected_fields": [],        # Champs requis dans la réponse
                "forbidden_fields": [],       # Champs interdits
                "expected_values": {},        # Valeurs exactes attendues
                "content_type": "text/html"   # Type de contenu
            }
        },
        
        "POST": {
            "enabled": True,
            "auth_required": True,
            "inverse_test": True,     # Activer test avec champs manquants
            
            "request": {
                "required_fields": [],      # Champs obligatoires
                "optional_fields": [],      # Champs facultatifs
                "exclude_fields": [],       # Champs interdits
                "len_fields": [             # Contraintes de longueur
                    {"field": "name", "min": 3, "max": 50}
                ],
                "type_fields": [            # Types attendus
                    {"field": "price", "type": float}
                ],
                "unique_fields": [],        # Champs uniques
                "default_values": {}        # Valeurs par défaut
            },
            
            "response": {
                "status_code": 302,
                "inverse_status_code": 400  # Code si champs manquants
            }
        }
    }
}

Options de langue

# Messages en français (par défaut)
config = AutoTestCaseConfig(lang="fr", ...)

# Messages en anglais
config = AutoTestCaseConfig(lang="en", ...)

🌍 Multilingue (FR/EN)

Django AutoTest supporte le français et l'anglais pour tous les messages d'erreur.

Messages d'assertion

Situation Français English
Status code incorrect Code de statut attendu 200, obtenu 404 Expected status code 200, got 404
Champ manquant Champ attendu "username" non trouvé Expected field "username" not found
Champ interdit Champ interdit "password" trouvé Forbidden field "password" found
Longueur invalide La longueur 2 du champ "name" est inférieure au minimum 3 Field "name" length 2 is less than minimum 3

Exemple

# Config en français
class TestFR(AutoTestCase):
    config = AutoTestCaseConfig(
        lang="fr",
        config={...}
    )

# Output : "Code de statut attendu 200, obtenu 404"

# Config en anglais
class TestEN(AutoTestCase):
    config = AutoTestCaseConfig(
        lang="en",
        config={...}
    )

# Output: "Expected status code 200, got 404"

🤝 Contribuer

Toutes les contributions sont les bienvenues ! Voici comment participer :

1. Fork et clone

git clone https://github.com/alzeph/Django-Auto-Test.git
cd Django-Auto-Test

2. Créer une branche

git checkout -b feature/ma-nouvelle-fonctionnalite

3. Faire vos modifications

  • Ajoutez vos fonctionnalités
  • Écrivez des tests
  • Documentez votre code

4. Soumettre une Pull Request

git add .
git commit -m "Ajout de ma fonctionnalité"
git push origin feature/ma-nouvelle-fonctionnalite

Puis ouvrez une PR sur GitHub avec une description claire.

Code style

  • Suivre PEP 8
  • Docstrings en français et anglais
  • Type hints quand possible
  • Tests unitaires obligatoires

🐛 Issues & Roadmap

🔴 Issues critiques (HELP WANTED!)

Voici les problèmes qui me bloquent actuellement. Si vous pouvez aider, vos PRs sont les bienvenues !

1. Bug dans les tests inverses

Problème : Les tests inverses (avec champs manquants) ne font actuellement rien (pass).

Fichier : djangoautotest_app/core/testcase.py, méthode _make_request()

Code actuel :

# Cas inverse : on enlève volontairement les champs requis
else:
    pass  # ❌ PROBLÈME : Rien n'est testé !

Ce qu'il faudrait :

  • Retirer seulement UN champ requis (pas tous)
  • Vérifier que la requête échoue avec le bon status code
  • Vérifier que le message d'erreur est présent

Difficulté : Facile

Fonctionnalités manquantes

Difficulté : Moyen

1. Validation des choix (choices) 🚧

Besoin : Tester que les valeurs font partie des choix autorisés

Config souhaitée :

"choice_fields": [
    {
        "field": "status",
        "choices": ["draft", "published", "archived"]
    }
]

Difficulté : Moyen


8. Tests unitaires du framework 🧪

Problème : Le framework n'a pas de tests pour lui-même !

Ce qu'il faudrait :

  • Tests unitaires pour ModelFactory
  • Tests unitaires pour AutoTestCaseConfig
  • Tests unitaires pour AutoTestCase
  • Coverage > 90%

Outils : pytest, pytest-django, pytest-cov

Difficulté : 🔴 Difficile


9. Documentation complète 📚

Problème : Pas de documentation Read The Docs

Ce qu'il faudrait :

  • Guide d'installation détaillé
  • Tutoriels pas à pas
  • Exemples pour chaque type de vue
  • API Reference
  • Contributing guide

Outils : Sphinx, Read The Docs

Difficulté : Moyen


10. Publication sur PyPI 📦

Problème : Installation manuelle uniquement

Ce qu'il faudrait :

  • Créer setup.py / pyproject.toml
  • Configurer versioning (semantic versioning)
  • Publier sur PyPI : pip install django-autotest
  • CI/CD avec GitHub Actions

Difficulté : Moyen


💡 Idées pour le futur

  • Support des formsets Django
  • Support des forms avec fichiers (FileField, ImageField)
  • Support des vues AJAX
  • Support des API REST (DRF)
  • Génération de rapports HTML de tests
  • Intégration avec coverage.py
  • Plugin pytest pour utiliser sans TestCase
  • Interface admin pour visualiser la couverture de tests

📄 Licence

MIT License

Copyright (c) 2025 [Votre Nom]

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


🙏 Remerciements

  • Django pour le framework web
  • Faker pour la génération de données
  • La communauté Django pour l'inspiration
  • Tous les contributeurs qui améliorent ce projet

📧 Contact


⭐ Si ce projet vous aide, n'hésitez pas à lui donner une étoile sur GitHub ! ⭐

Made with ❤️ by [Votre Nom]

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_autotest_all-0.1.0.tar.gz (25.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_autotest_all-0.1.0-py3-none-any.whl (21.6 kB view details)

Uploaded Python 3

File details

Details for the file django_autotest_all-0.1.0.tar.gz.

File metadata

  • Download URL: django_autotest_all-0.1.0.tar.gz
  • Upload date:
  • Size: 25.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.9

File hashes

Hashes for django_autotest_all-0.1.0.tar.gz
Algorithm Hash digest
SHA256 2f80eb7dba9b3739bba6df39545cd717f286c4ba8393b5f5bd2d29dfd0aa28c4
MD5 2aeb397d40356da708ec5db75f9df608
BLAKE2b-256 c38daf2c533a208d29642ad2ad2c9139ea4a15a304a046cf5638d4782eeb37db

See more details on using hashes here.

File details

Details for the file django_autotest_all-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for django_autotest_all-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 72b6211618c21e21ede0ad1ef61e29ec1609eebc534ac4bcec07d96c1412d2ed
MD5 44d85ef48288544de96bdd771c28601b
BLAKE2b-256 66db275ee62909b4ebd514a70db700fc3dd7bc14d35ebfb4578c145d850b3f0d

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page