Pour réaliser ce TP, allez sur la branche 2_start_unit_test
git stash
git checkout 2_start_unit_test
Nouveauté sur cette branche :
L'objectif de ce TP est d'écrire quelques tests unitaires.
uv add pytest --dev
Pour écrire des tests, nous allons :
pytest.tests à la racine qui va contenir l'ensemble des teststest_feature_engineering.py. En python la convention veut que les fichiers de tests :test_.Créer une fonction test_process_name selon le template suivant :
def test_process_name():
# Given
# When
# Then
Écrire le then en premier, nous allons nous assurer que le résultat est bien celui attendu, en comparant 2 data
frames.
import pandas as pd
def test_process_name():
# Given
# When
# Then
pd.testing.assert_frame_equal(expected_df, result_df)
Commencez par le Then permet de s'assurer que l'on sait ce que l'on veut valider
Écrire le when : l'appel à la fonction
import pandas as pd
from src.feature_engineering import process_name
def test_process_name():
# Given
# When
result_df = process_name(df)
# Then
pd.testing.assert_frame_equal(expected_df, result_df)
Finir par écrire le given
import pandas as pd
from src.feature_engineering import process_name
def test_process_name():
# Given
df = pd.DataFrame({"Name": ["Braund, Mr. Owen Harris"]})
expected_df = pd.DataFrame({"Name": ["Braund, Mr. Owen Harris"],
"Name_Len": [23],
"Name_Title": "Mr."})
# When
result_df = process_name(df)
# Then
pd.testing.assert_frame_equal(expected_df, result_df)
Pour écrire les données de tests, nous recommandons (si ce ne sont pas des données personnelles ou confidentielles) d'utiliser des données de la production.
Exécuter les tests en cliquant sur la petite flèche verte à côté du nom de la fonction ou lancer la commande uv run pytest ou pytest
Pour vous exercer, écrivez 3 autres tests automatisés sur d'autres fonctions.
Les tests c'est bien, mais autant s'assurer aussi que le code est propre avant de le tester. C'est le rôle du linting : vérifier automatiquement le style et les erreurs courantes.
On va utiliser ruff, un linter Python ultra-rapide, déjà configuré dans le pyproject.toml.
uv add ruff --dev
uv run ruff check src tests
Si tout va bien, aucune sortie. Sinon, ruff vous indique les lignes à corriger.
uv run ruff check src tests --fix
Jetez un œil à la section [tool.ruff] dans pyproject.toml pour voir les règles activées. On y retrouve entre autres les vérifications de style (pycodestyle), les imports (isort), et les bugs courants (flake8-bugbear).
Plutôt que de retenir toutes les commandes uv run ..., un Makefile à la racine du projet centralise les tâches courantes. Pour voir les commandes disponibles :
make help
Quelques exemples :
make install — installe les dépendancesmake lint — lance le linting (ruff, bandit, vulture)make test — lance les tests unitairesL'avantage : tout le monde utilise les mêmes commandes, et la CI (cf section suivante) peut aussi s'appuyer dessus. Pas besoin de se souvenir des options de chaque outil.
La CI permet de valider les tests et linting selon un événement sur le repos. Regardez .github/workflows/ci.yml pour un exemple de configuration.
C'est bien de lancer le linting et les tests à la main, mais on oublie vite. L'idée du pre-commit hook, c'est de les lancer automatiquement à chaque git commit. Si ça échoue, le commit est bloqué — impossible de pousser du code qui ne passe pas les vérifications.
.githooks à la racine du projet et y ajouter un fichier pre-commit :mkdir -p .githooks
# .githooks/pre-commit
#!/bin/bash
set -e
echo "🔍 Running lint..."
make lint
echo "🧪 Running tests..."
make test
echo "✅ All checks passed!"
chmod +x .githooks/pre-commit
git config core.hooksPath .githooks
Ou plus simplement, si la target existe dans le Makefile :
make install-hooks
git commit. Vous devriez voir le lint et les tests se lancer automatiquement. Si l'un des deux échoue, le commit sera refusé.Astuce : en cas d'urgence, vous pouvez contourner le hook avec git commit --no-verify, mais c'est à utiliser avec parcimonie
Les instructions du TP suivant sont ici