feat: add unit tests for core functions
Browse files- tests/__init__.py +0 -0
- tests/test_functional.py +0 -0
- tests/test_unit.py +157 -0
tests/__init__.py
ADDED
|
File without changes
|
tests/test_functional.py
ADDED
|
File without changes
|
tests/test_unit.py
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pandas as pd
|
| 2 |
+
import pytest
|
| 3 |
+
from app.main import depart, inconsistency, promotion, developpement, interpret_shap
|
| 4 |
+
import os
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
# ---------------------------------------------------------------------------
|
| 8 |
+
# depart
|
| 9 |
+
# ---------------------------------------------------------------------------
|
| 10 |
+
|
| 11 |
+
def test_depart():
|
| 12 |
+
assert depart(0) == "The staff has a LOW probability of resigning"
|
| 13 |
+
assert depart(1) == "The staff has a HIGH probability of resigning"
|
| 14 |
+
|
| 15 |
+
# ---------------------------------------------------------------------------
|
| 16 |
+
# inconsistency
|
| 17 |
+
# The function is called via df.apply(inconsistency, axis=1), so each input
|
| 18 |
+
# is a pandas Series representing a single row.
|
| 19 |
+
# ---------------------------------------------------------------------------
|
| 20 |
+
|
| 21 |
+
class TestInconsistency:
|
| 22 |
+
# --- Commercial ---
|
| 23 |
+
def test_commercial_marketing_is_consistent(self):
|
| 24 |
+
row = pd.Series({"departement": "Commercial", "domaine_etude": "Marketing"})
|
| 25 |
+
assert inconsistency(row) == 0
|
| 26 |
+
|
| 27 |
+
def test_commercial_infra_is_inconsistent(self):
|
| 28 |
+
row = pd.Series({"departement": "Commercial", "domaine_etude": "Infra & Cloud"})
|
| 29 |
+
assert inconsistency(row) == 1
|
| 30 |
+
|
| 31 |
+
def test_commercial_rh_is_inconsistent(self):
|
| 32 |
+
row = pd.Series({"departement": "Commercial", "domaine_etude": "Ressources Humaines"})
|
| 33 |
+
assert inconsistency(row) == 1
|
| 34 |
+
|
| 35 |
+
def test_commercial_transformation_is_inconsistent(self):
|
| 36 |
+
row = pd.Series({"departement": "Commercial", "domaine_etude": "Transformation Digitale"})
|
| 37 |
+
assert inconsistency(row) == 1
|
| 38 |
+
|
| 39 |
+
# --- Consulting ---
|
| 40 |
+
def test_consulting_infra_is_consistent(self):
|
| 41 |
+
row = pd.Series({"departement": "Consulting", "domaine_etude": "Infra & Cloud"})
|
| 42 |
+
assert inconsistency(row) == 0
|
| 43 |
+
|
| 44 |
+
def test_consulting_transformation_is_consistent(self):
|
| 45 |
+
row = pd.Series({"departement": "Consulting", "domaine_etude": "Transformation Digitale"})
|
| 46 |
+
assert inconsistency(row) == 0
|
| 47 |
+
|
| 48 |
+
def test_consulting_marketing_is_inconsistent(self):
|
| 49 |
+
row = pd.Series({"departement": "Consulting", "domaine_etude": "Marketing"})
|
| 50 |
+
assert inconsistency(row) == 1
|
| 51 |
+
|
| 52 |
+
def test_consulting_rh_is_inconsistent(self):
|
| 53 |
+
row = pd.Series({"departement": "Consulting", "domaine_etude": "Ressources Humaines"})
|
| 54 |
+
assert inconsistency(row) == 1
|
| 55 |
+
|
| 56 |
+
# --- Ressources Humaines ---
|
| 57 |
+
def test_rh_rh_is_consistent(self):
|
| 58 |
+
row = pd.Series({"departement": "Ressources Humaines", "domaine_etude": "Ressources Humaines"})
|
| 59 |
+
assert inconsistency(row) == 0
|
| 60 |
+
|
| 61 |
+
def test_rh_entrepreneuriat_is_consistent(self):
|
| 62 |
+
row = pd.Series({"departement": "Ressources Humaines", "domaine_etude": "Entrepreunariat"})
|
| 63 |
+
assert inconsistency(row) == 0
|
| 64 |
+
|
| 65 |
+
def test_rh_marketing_is_inconsistent(self):
|
| 66 |
+
row = pd.Series({"departement": "Ressources Humaines", "domaine_etude": "Marketing"})
|
| 67 |
+
assert inconsistency(row) == 1
|
| 68 |
+
|
| 69 |
+
def test_rh_infra_is_inconsistent(self):
|
| 70 |
+
row = pd.Series({"departement": "Ressources Humaines", "domaine_etude": "Infra & Cloud"})
|
| 71 |
+
assert inconsistency(row) == 1
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
# ---------------------------------------------------------------------------
|
| 75 |
+
# promotion
|
| 76 |
+
# Returns 1 (stagnant) only when BOTH values are strictly greater than 4.
|
| 77 |
+
# ---------------------------------------------------------------------------
|
| 78 |
+
|
| 79 |
+
class TestPromotion:
|
| 80 |
+
def test_both_above_4_is_stagnant(self):
|
| 81 |
+
row = pd.Series({"annes_sous_responsable_actuel": 5, "annees_depuis_la_derniere_promotion": 5})
|
| 82 |
+
assert promotion(row) == 1
|
| 83 |
+
|
| 84 |
+
def test_large_values_are_stagnant(self):
|
| 85 |
+
row = pd.Series({"annes_sous_responsable_actuel": 10, "annees_depuis_la_derniere_promotion": 10})
|
| 86 |
+
assert promotion(row) == 1
|
| 87 |
+
|
| 88 |
+
def test_manager_years_exactly_4_not_stagnant(self):
|
| 89 |
+
row = pd.Series({"annes_sous_responsable_actuel": 4, "annees_depuis_la_derniere_promotion": 5})
|
| 90 |
+
assert promotion(row) == 0
|
| 91 |
+
|
| 92 |
+
def test_promotion_years_exactly_4_not_stagnant(self):
|
| 93 |
+
row = pd.Series({"annes_sous_responsable_actuel": 5, "annees_depuis_la_derniere_promotion": 4})
|
| 94 |
+
assert promotion(row) == 0
|
| 95 |
+
|
| 96 |
+
def test_both_below_threshold_not_stagnant(self):
|
| 97 |
+
row = pd.Series({"annes_sous_responsable_actuel": 2, "annees_depuis_la_derniere_promotion": 2})
|
| 98 |
+
assert promotion(row) == 0
|
| 99 |
+
|
| 100 |
+
def test_only_manager_years_high_not_stagnant(self):
|
| 101 |
+
row = pd.Series({"annes_sous_responsable_actuel": 6, "annees_depuis_la_derniere_promotion": 1})
|
| 102 |
+
assert promotion(row) == 0
|
| 103 |
+
|
| 104 |
+
def test_only_promotion_years_high_not_stagnant(self):
|
| 105 |
+
row = pd.Series({"annes_sous_responsable_actuel": 1, "annees_depuis_la_derniere_promotion": 6})
|
| 106 |
+
assert promotion(row) == 0
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
# ---------------------------------------------------------------------------
|
| 110 |
+
# developpement
|
| 111 |
+
# Returns 0 for new employees (tenure == 0), 1 when tenure >= 2 with <= 1
|
| 112 |
+
# training, 0 otherwise (sufficient training, or tenure == 1 year).
|
| 113 |
+
# ---------------------------------------------------------------------------
|
| 114 |
+
|
| 115 |
+
class TestDeveloppement:
|
| 116 |
+
def test_new_employee_returns_0(self):
|
| 117 |
+
row = pd.Series({"annees_dans_l_entreprise": 0, "nb_formations_suivies": 0})
|
| 118 |
+
assert developpement(row) == 0
|
| 119 |
+
|
| 120 |
+
def test_one_year_tenure_returns_0(self):
|
| 121 |
+
# tenure == 1 meets neither condition β 0
|
| 122 |
+
row = pd.Series({"annees_dans_l_entreprise": 1, "nb_formations_suivies": 0})
|
| 123 |
+
assert developpement(row) == 0
|
| 124 |
+
|
| 125 |
+
def test_two_years_no_training_is_stagnant(self):
|
| 126 |
+
row = pd.Series({"annees_dans_l_entreprise": 2, "nb_formations_suivies": 0})
|
| 127 |
+
assert developpement(row) == 1
|
| 128 |
+
|
| 129 |
+
def test_two_years_one_training_is_stagnant(self):
|
| 130 |
+
row = pd.Series({"annees_dans_l_entreprise": 2, "nb_formations_suivies": 1})
|
| 131 |
+
assert developpement(row) == 1
|
| 132 |
+
|
| 133 |
+
def test_two_years_two_trainings_not_stagnant(self):
|
| 134 |
+
row = pd.Series({"annees_dans_l_entreprise": 2, "nb_formations_suivies": 2})
|
| 135 |
+
assert developpement(row) == 0
|
| 136 |
+
|
| 137 |
+
def test_long_tenure_no_training_is_stagnant(self):
|
| 138 |
+
row = pd.Series({"annees_dans_l_entreprise": 10, "nb_formations_suivies": 0})
|
| 139 |
+
assert developpement(row) == 1
|
| 140 |
+
|
| 141 |
+
def test_long_tenure_sufficient_training_not_stagnant(self):
|
| 142 |
+
row = pd.Series({"annees_dans_l_entreprise": 10, "nb_formations_suivies": 5})
|
| 143 |
+
assert developpement(row) == 0
|
| 144 |
+
|
| 145 |
+
|
| 146 |
+
def test_interpret_shap():
|
| 147 |
+
assert interpret_shap(0, 0.5) == "Primary driver β increases resignation risk"
|
| 148 |
+
assert interpret_shap(0, -0.5) == "Primary driver β decreases resignation risk"
|
| 149 |
+
assert interpret_shap(1, 0.5) == "Strong factor β increases resignation risk"
|
| 150 |
+
assert interpret_shap(1, -0.5) == "Strong factor β decreases resignation risk"
|
| 151 |
+
assert interpret_shap(2, 0.5) == "Moderate factor β increases resignation risk"
|
| 152 |
+
assert interpret_shap(2, -0.5) == "Moderate factor β decreases resignation risk"
|
| 153 |
+
assert interpret_shap(3, 0.5) == "Contributing factor β increases resignation risk"
|
| 154 |
+
assert interpret_shap(3, -0.5) == "Contributing factor β decreases resignation risk"
|
| 155 |
+
assert interpret_shap(4, 0.5) == "Notable factor β increases resignation risk"
|
| 156 |
+
assert interpret_shap(4, -0.5) == "Notable factor β decreases resignation risk"
|
| 157 |
+
|