Merge pull request #4 from Tbruand/test/interface
Browse filesfeat(interface): merge des tests d’interface, couverture et hook pre-push
Cette fusion intègre :
- Les tests unitaires pour `app/handler.py` et `app/interface.py`
- La mise en place d’un hook pre-push pour valider les tests avant chaque push
- L’intégration de `pytest-cov` pour mesurer la couverture du code
- La création d’un `README.md` dédié dans `tests/` documentant :
- Les fichiers testés
- Les commandes pour lancer les tests
- La couverture actuelle (91%) et justification des lignes non testées
- app/interface.py +5 -2
- requirements.txt +1 -0
- tests/README.md +62 -0
- tests/test_interface.py +33 -0
- tox.ini +4 -1
app/interface.py
CHANGED
@@ -1,8 +1,8 @@
|
|
1 |
import gradio as gr
|
2 |
from app.handler import predict
|
3 |
|
4 |
-
def
|
5 |
-
|
6 |
fn=predict,
|
7 |
inputs=[
|
8 |
gr.Textbox(label="Texte à analyser"),
|
@@ -12,4 +12,7 @@ def launch_app():
|
|
12 |
title="🧪 ToxiCheck",
|
13 |
description="Entrez un texte pour détecter s'il est toxique. Résultat avec score de confiance pour chaque label."
|
14 |
)
|
|
|
|
|
|
|
15 |
iface.launch()
|
|
|
1 |
import gradio as gr
|
2 |
from app.handler import predict
|
3 |
|
4 |
+
def create_interface():
|
5 |
+
return gr.Interface(
|
6 |
fn=predict,
|
7 |
inputs=[
|
8 |
gr.Textbox(label="Texte à analyser"),
|
|
|
12 |
title="🧪 ToxiCheck",
|
13 |
description="Entrez un texte pour détecter s'il est toxique. Résultat avec score de confiance pour chaque label."
|
14 |
)
|
15 |
+
|
16 |
+
def launch_app():
|
17 |
+
iface = create_interface()
|
18 |
iface.launch()
|
requirements.txt
CHANGED
@@ -4,4 +4,5 @@ torch
|
|
4 |
scikit-learn
|
5 |
pandas
|
6 |
pytest
|
|
|
7 |
commitizen
|
|
|
4 |
scikit-learn
|
5 |
pandas
|
6 |
pytest
|
7 |
+
pytest-cov
|
8 |
commitizen
|
tests/README.md
ADDED
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
## ✅ Tests unitaires & couverture
|
2 |
+
|
3 |
+
Ce projet dispose de tests automatisés pour valider les principales fonctionnalités :
|
4 |
+
|
5 |
+
### 📂 Fichiers de test
|
6 |
+
|
7 |
+
| Fichier | Description |
|
8 |
+
| ------------------------- | --------------------------------------------------------------------------------------------------------------------- |
|
9 |
+
| `tests/test_handler.py` | Vérifie le bon fonctionnement de la fonction `predict()` en mode `zero-shot` et `few-shot`. |
|
10 |
+
| `tests/test_interface.py` | Teste l'interface Gradio, les comportements inattendus (entrée vide, modèle invalide), et la création de l'interface. |
|
11 |
+
|
12 |
+
---
|
13 |
+
|
14 |
+
### 🧪 Lancer les tests
|
15 |
+
|
16 |
+
```bash
|
17 |
+
python -m pytest --cov=app --cov-report=term-missing
|
18 |
+
```
|
19 |
+
|
20 |
+
Ce qui génère un rapport de couverture avec les lignes non couvertes :
|
21 |
+
|
22 |
+
```
|
23 |
+
Name Stmts Miss Cover Missing
|
24 |
+
------------------------------------------------
|
25 |
+
app/handler.py 16 0 100%
|
26 |
+
app/interface.py 7 2 71% 17-18
|
27 |
+
------------------------------------------------
|
28 |
+
TOTAL 23 2 91%
|
29 |
+
```
|
30 |
+
|
31 |
+
> 🌟 Les lignes 17-18 non couvertes correspondent à l’exécution directe de l’interface dans le fichier `main.py`.
|
32 |
+
> Ces lignes ne sont volontairement **pas testées** car elles concernent le lancement interactif de l'application (`iface.launch()`), hors du périmètre des tests unitaires.
|
33 |
+
|
34 |
+
---
|
35 |
+
|
36 |
+
### 🚫 Hook `pre-push` automatique
|
37 |
+
|
38 |
+
Pour garantir la stabilité du dépôt, un **hook Git `pre-push`** a été mis en place.
|
39 |
+
|
40 |
+
🧹 Il exécute automatiquement les tests avant chaque `git push`.
|
41 |
+
|
42 |
+
#### Exemple `.git/hooks/pre-push`
|
43 |
+
|
44 |
+
```bash
|
45 |
+
#!/bin/sh
|
46 |
+
echo "🔍 Exécution des tests unitaires avant le push..."
|
47 |
+
python -m pytest --cov=app --cov-report=term-missing
|
48 |
+
if [ $? -ne 0 ]; then
|
49 |
+
echo "❌ Push bloqué : les tests ont échoué."
|
50 |
+
exit 1
|
51 |
+
fi
|
52 |
+
```
|
53 |
+
|
54 |
+
#### 🔧 Installation manuelle
|
55 |
+
|
56 |
+
1. Crée un fichier `.git/hooks/pre-push` si ce n’est pas déjà fait,
|
57 |
+
2. Colle le script ci-dessus,
|
58 |
+
3. Rends-le exécutable :
|
59 |
+
|
60 |
+
```bash
|
61 |
+
chmod +x .git/hooks/pre-push
|
62 |
+
```
|
tests/test_interface.py
CHANGED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import sys
|
2 |
+
import os
|
3 |
+
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
4 |
+
|
5 |
+
from app.interface import predict
|
6 |
+
|
7 |
+
def test_predict_zero_shot():
|
8 |
+
result = predict("Tu es gentil.", model_type="zero-shot")
|
9 |
+
assert isinstance(result, str)
|
10 |
+
assert "Résultat de la classification" in result
|
11 |
+
|
12 |
+
def test_predict_few_shot():
|
13 |
+
result = predict("Tu es débile.", model_type="few-shot")
|
14 |
+
assert isinstance(result, str)
|
15 |
+
assert "Résultat de la classification" in result
|
16 |
+
|
17 |
+
def test_predict_empty_input():
|
18 |
+
try:
|
19 |
+
result = predict("", model_type="zero-shot")
|
20 |
+
except ValueError as e:
|
21 |
+
assert "at least one sequence" in str(e)
|
22 |
+
|
23 |
+
def test_predict_invalid_model():
|
24 |
+
try:
|
25 |
+
predict("Texte test", model_type="unknown")
|
26 |
+
except ValueError as e:
|
27 |
+
assert "Modèle inconnu" in str(e)
|
28 |
+
|
29 |
+
def test_create_interface():
|
30 |
+
from app.interface import create_interface
|
31 |
+
iface = create_interface()
|
32 |
+
assert iface.fn is not None
|
33 |
+
assert len(iface.input_components) == 2
|
tox.ini
CHANGED
@@ -1,3 +1,6 @@
|
|
1 |
[pytest]
|
2 |
testpaths = tests
|
3 |
-
python_files = test_*.py
|
|
|
|
|
|
|
|
1 |
[pytest]
|
2 |
testpaths = tests
|
3 |
+
python_files = test_*.py
|
4 |
+
filterwarnings =
|
5 |
+
ignore::DeprecationWarning
|
6 |
+
addopts = --cov=app
|