Structurez des tests robustes avec pytest : injection de fixtures, démontage par yield, paramétrage en table de cas et pièges des scopes larges.
Ouvrir cette leçon dans Kodokonpytest repose sur deux idées fortes : la découverte par convention (fichiers test_*.py, fonctions test_*) et le simple assert, dont l'introspection affiche les valeurs réelles des deux membres en cas d'échec - plus besoin de mémoriser assertEqual et ses trente variantes. Deux outils à connaître d'emblée : pytest.raises pour vérifier qu'une exception part bien, et pytest.approx pour comparer des flottants sans vous battre avec les arrondis binaires.
import pytest
def divide(a: float, b: float) -> float:
return a / b
def test_nominal() -> None:
assert divide(8, 4) == 2.0
def test_zero_division() -> None:
with pytest.raises(ZeroDivisionError):
divide(1, 0)
def test_floats() -> None:
assert 0.1 + 0.2 == pytest.approx(0.3)Les fixtures sont l'injection de dépendances de pytest : déclarez un paramètre portant le nom d'une fixture, et pytest la construit puis l'injecte. Le yield sépare préparation et démontage - le code situé après s'exécute même si le test échoue. Le paramètre scope (function par défaut, module, session) contrôle la durée de vie de l'objet. Placez les fixtures partagées dans un conftest.py : elles deviennent disponibles dans tout le dossier, sans le moindre import.
import sqlite3
import pytest
@pytest.fixture
def db():
conn = sqlite3.connect(":memory:")
conn.execute("CREATE TABLE users (name TEXT)")
yield conn
conn.close()
def test_insert(db) -> None:
db.execute("INSERT INTO users VALUES ('Ada')")
query = "SELECT name FROM users"
rows = db.execute(query).fetchall()
assert rows == [("Ada",)]@pytest.mark.parametrize transforme un test en table de cas : chaque tuple devient un test indépendant, nommé et filtrable individuellement. Empilez plusieurs décorateurs parametrize pour obtenir le produit cartésien des paramètres. Donnez des id lisibles à vos cas via pytest.param(..., id='...') - un rapport d'échec doit se comprendre sans ouvrir le fichier source.
import pytest
def slugify(text: str) -> str:
return "-".join(text.lower().split())
@pytest.mark.parametrize(
("raw", "expected"),
[
("Hello World", "hello-world"),
(" Python rocks ", "python-rocks"),
pytest.param("", "", id="empty"),
],
)
def test_slugify(raw: str, expected: str) -> None:
assert slugify(raw) == expectedpytest -x --lf
pytest -k "slugify and not empty"
pytest -q --durations=5yield ?pytest --lf ?