Spaces:
Sleeping
Sleeping
test: add model integration and threshold tests
Browse files- tests/test_model.py +66 -0
tests/test_model.py
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pytest
|
| 2 |
+
from unittest.mock import MagicMock, patch
|
| 3 |
+
from fastapi.testclient import TestClient
|
| 4 |
+
|
| 5 |
+
from ml_model.loader import load_pipeline
|
| 6 |
+
from app.main import app
|
| 7 |
+
from tests.conftest import VALID_PAYLOAD, TEST_API_KEY
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
# Fixture : client avec le VRAI pipeline (pas de mock)
|
| 11 |
+
@pytest.fixture(scope="module")
|
| 12 |
+
def real_client():
|
| 13 |
+
with patch.dict("os.environ", {"API_KEY": TEST_API_KEY}):
|
| 14 |
+
with TestClient(app) as c:
|
| 15 |
+
yield c
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
# Tester que le pipeline .pkl se charge correctement
|
| 19 |
+
def test_load_pipeline():
|
| 20 |
+
pipeline, threshold = load_pipeline()
|
| 21 |
+
assert pipeline is not None
|
| 22 |
+
assert isinstance(threshold, float)
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
# Tester que la prédiction retourne bien "Reste" ou "Quitte"
|
| 26 |
+
def test_predict_label_valid(real_client):
|
| 27 |
+
response = real_client.post(
|
| 28 |
+
"/predict",
|
| 29 |
+
json=VALID_PAYLOAD,
|
| 30 |
+
headers={"X-API-Key": TEST_API_KEY},
|
| 31 |
+
)
|
| 32 |
+
assert response.status_code == 200
|
| 33 |
+
assert response.json()["label"] in ("Reste", "Quitte")
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
# Tester que la probabilité est bien comprise entre 0 et 1
|
| 37 |
+
def test_predict_probabilite_range(real_client):
|
| 38 |
+
response = real_client.post(
|
| 39 |
+
"/predict",
|
| 40 |
+
json=VALID_PAYLOAD,
|
| 41 |
+
headers={"X-API-Key": TEST_API_KEY},
|
| 42 |
+
)
|
| 43 |
+
assert 0.0 <= response.json()["probabilite"] <= 1.0
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
# Tester que le seuil 0.40 est bien appliqué
|
| 47 |
+
@pytest.mark.parametrize("proba_quitte,expected_label", [
|
| 48 |
+
(0.50, "Quitte"), # clairement au-dessus du seuil
|
| 49 |
+
(0.30, "Reste"), # clairement en-dessous du seuil
|
| 50 |
+
])
|
| 51 |
+
def test_threshold_applied(proba_quitte, expected_label):
|
| 52 |
+
_, threshold = load_pipeline() # ← vrai seuil (0.3999...7)
|
| 53 |
+
mock_pipeline = MagicMock()
|
| 54 |
+
mock_pipeline.predict_proba.return_value = [[1 - proba_quitte, proba_quitte]]
|
| 55 |
+
|
| 56 |
+
with patch("app.main.load_pipeline", return_value=(mock_pipeline, threshold)), \
|
| 57 |
+
patch.dict("os.environ", {"API_KEY": TEST_API_KEY}):
|
| 58 |
+
with TestClient(app) as c:
|
| 59 |
+
response = c.post(
|
| 60 |
+
"/predict",
|
| 61 |
+
json=VALID_PAYLOAD,
|
| 62 |
+
headers={"X-API-Key": TEST_API_KEY},
|
| 63 |
+
)
|
| 64 |
+
|
| 65 |
+
assert response.json()["label"] == expected_label
|
| 66 |
+
|