django-autotest-all 0.1.0
Une application réutilisable pour tester les vues django
License: MIT
Requires Python: >=3.8

# 🚀 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.

```python
# 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 ?](#-pourquoi-django-autotest-)
- [Fonctionnalités](#-fonctionnalités)
- [Installation](#-installation)
- [Utilisation rapide](#-utilisation-rapide)
- [Exemples détaillés](#-exemples-détaillés)
- [Architecture](#-architecture)
- [Configuration complète](#-configuration-complète)
- [Multilingue (FR/EN)](#-multilingue-fren)
- [Contribuer](#-contribuer)
- [Issues & Roadmap](#-issues--roadmap)
- [Licence](#-licence)

---

## 🎯 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

```bash
# 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

```python
# settings.py
INSTALLED_APPS = [
    # ...
    'djangoautotest_app',
]
```

---

## ⚡ Utilisation rapide

### Exemple 1 : Test d'une ListView

```python
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

```python
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

```python
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

```python
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.

```python
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.

```python
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.

```python
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

```python
{
    "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

```python
# 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

```python
# 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

```bash
git clone https://github.com/alzeph/Django-Auto-Test.git
cd Django-Auto-Test
```

### 2. Créer une branche

```bash
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

```bash
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** :
```python
# 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** :
```python
"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

- **Issues** : [GitHub Issues](https://github.com/alzeph/Django-Auto-Test/issues)
- **Discussions** : [GitHub Discussions](https://github.com/alzeph/Django-Auto-Test/discussions)
- **Email** : hervecedricyouan@gmail.com

---

<div align="center">

**⭐ Si ce projet vous aide, n'hésitez pas à lui donner une étoile sur GitHub ! ⭐**

Made with ❤️ by [Votre Nom]

</div>