Nouveau : Datasets open source gratuits disponibles !Decouvrir →
🧪
Intermediaire 25 min FastAPI

Tester FastAPI avec Pytest

Pourquoi Tester FastAPI avec Pytest ?

Tester FastAPI avec Pytest est essentiel pour les développeurs car il permet de s'assurer que l'application fonctionne comme prévu et qu'elle reste robuste à mesure du temps. En outre, cela aide à détecter rapidement les bugs ou les anomalies avant qu'ils ne deviennent problématiques.

Un cas d'usage concret est lorsque vous travaillez sur une API de gestion des utilisateurs. Un test automatique peut vérifier que le système crée un utilisateur avec succès et renvoie la bonne réponse HTTP, tout en s'assurant que les données sont correctement stockées dans la base de données.

Prerequis

  • Connaissances en FastAPI
  • Connaissance de Python (3.7+ recommandé)
  • Installation de Pytest (pip install pytest)
  • Un environnement de développement (IDE, éditeur de texte)

Concepts fondamentaux

1. Structure du Test

La structure d'un test est simple : il doit appeler une fonction qui teste une partie spécifique du code et vérifier les résultats.

## myapp/tests/test_main.py
import pytest
from main import app

def test_read_root():
    response = app.test_client().get("/")
    assert response.status_code == 200
    assert response.json() == {"message": "Hello World"}

2. Fixtures

Les fixtures sont des fonctions qui fournissent des données ou des objets que les tests utilisent. Elles facilitent la réutilisation du code et permettent une meilleure isolation.

## myapp/tests/conftest.py
from fastapi.testclient import TestClient
import main

@pytest.fixture(scope="module")
def test_client():
    app = main.app
    with TestClient(app) as client:
        yield client

3. Assertions

Pytest utilise des assertions pour vérifier les conditions de tes tests.

## myapp/tests/test_main.py
import pytest
from main import app

def test_read_root(test_client):
    response = test_client.get("/")
    assert response.status_code == 200
    assert response.json() == {"message": "Hello World"}

4. Paramètres de Tests

Pytest prend en charge les tests paramétrisés, ce qui permet d'exécuter le même test avec différents arguments.

## myapp/tests/test_main.py
import pytest
from main import app

@pytest.mark.parametrize("path, expected", [
    ("/", {"message": "Hello World"}),
    ("/items/1", {"item_id": 1}),
])
def test_read_path(test_client, path, expected):
    response = test_client.get(path)
    assert response.status_code == 200
    assert response.json() == expected

Mise en pratique : Projet fil rouge

Dans ce tutoriel, nous allons créer un simple gestionnaire de tâches avec FastAPI et Pytest. L'application permettra d'afficher une liste de tâches et de les marquer comme terminées.

Étape 1 : Configuration du projet

Créez un nouveau dossier pour le projet et initialisez-le avec Python.

mkdir task_manager
cd task_manager
python3 -m venv venv
source venv/bin/activate
pip install fastapi uvicorn pytest

Étape 2 : Création de l'application FastAPI

Créez un fichier main.py pour définir les routes et le modèle.

## myapp/main.py
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel

app = FastAPI()

class Task(BaseModel):
    id: int
    description: str
    completed: bool = False

tasks = []

@app.get("/")
async def read_root():
    return {"message": "Bienvenue au Gestionnaire de Tâches"}

@app.post("/tasks/", response_model=Task)
async def create_task(task: Task):
    tasks.append(task)
    return task

@app.get("/tasks/{task_id}", response_model=Task)
async def read_task(task_id: int):
    for task in tasks:
        if task.id == task_id:
            return task
    raise HTTPException(status_code=404, detail="Tâche non trouvée")

Étape 3 : Création des tests

Créez un dossier tests et ajoutez les fichiers de test.

## myapp/tests/test_main.py
import pytest
from main import app, tasks, Task
from fastapi.testclient import TestClient

@pytest.fixture(scope="module")
def test_client():
    with TestClient(app) as client:
        yield client

@pytest.fixture(autouse=True)
def clean_tasks():
    global tasks
    tasks = []

def test_read_root(test_client):
    response = test_client.get("/")
    assert response.status_code == 200
    assert response.json() == {"message": "Bienvenue au Gestionnaire de Tâches"}

def test_create_task(test_client):
    task_data = Task(id=1, description="Faire les courses")
    response = test_client.post("/tasks/", json=task_data.dict())
    assert response.status_code == 200
    assert response.json() == task_data.dict()

def test_read_task(test_client):
    task_data = Task(id=1, description="Faire les courses", completed=True)
    response = test_client.post("/tasks/", json=task_data.dict())
    assert response.status_code == 200

    read_response = test_client.get(f"/tasks/{task_data.id}")
    assert read_response.status_code == 200
    assert read_response.json() == task_data.dict()

Étape 4 : Exécution des tests

Exécutez les tests avec la commande suivante :

pytest myapp/tests/

Erreurs fréquentes et debugging

Erreur 1 : AttributeError: 'NoneType' object has no attribute 'status_code'

Cela se produit si vous essayez d'accéder à un attribut de la réponse avant qu'elle ne soit initialisée.

## ❌ Mauvais
response = app.test_client().get("/")
assert response.status_code == 200  # Erreur car app.test_client() est None

## ✅ Correct
client = TestClient(app)
response = client.get("/")
assert response.status_code == 200

Erreur 2 : KeyError: 'task_id'

Cela se produit si vous essayez d'accéder à une clé qui n'existe pas dans le dictionnaire de la réponse.

## ❌ Mauvais
response = test_client.get("/tasks/1")
assert response.json()["task_id"] == 1  # Erreur car 'task_id' n'existe pas

## ✅ Correct
response = test_client.get("/tasks/1")
assert response.status_code == 200
assert response.json()["id"] == 1

Erreur 3 : AssertionError: assert False

Cela se produit si votre assertion échoue.

## ❌ Mauvais
response = test_client.get("/tasks/1")
assert response.status_code == 204  # Erreur car le code est 200

## ✅ Correct
response = test_client.get("/tasks/1")
assert response.status_code == 200

Pour aller plus loin

1. Tests Asynchrones avec pytest-asyncio

Pour tester des applications FastAPI asynchrones, utilisez le plugin pytest-asyncio.

pip install pytest-asyncio

Ajoutez la configuration suivante dans votre fichier pytest.ini :

[pytest]
addopts = --asyncio-mode=auto

2. Coverage des Tests

Utilisez un outil de couverture pour vérifier combien de code est testé.

pip install pytest-cov
pytest myapp/tests/ --cov=myapp

3. Fixtures Avancées

Explorez les fixtures avancées comme autouse, scope="session", et les fixtures paramétrisées.

Défi Pratique

Créez un API simple de blog avec FastAPI qui permet d'ajouter, lire, mettre à jour et supprimer des articles. Ajoutez des tests pour chaque fonctionnalité.

Besoin d'aide sur FastAPI ?

Besoin d'aide sur un projet technique ? Decrivez-le pour des conseils personnalises.

Recevoir des conseils

Questions frequentes

Comment installer FastAPI et Pytest dans un environnement virtuel ?
Pour installer FastAPI et Pytest, vous devez d'abord créer un environnement virtuel avec `python -m venv env`. Ensuite, activez l'environnement virtuel et installez les packages avec `pip install fastapi pytest`.
Quelle est la structure de base d'une fonction de test en utilisant Pytest pour FastAPI ?
Une fonction de test en Pytest pour FastAPI devrait commencer par l'annotation `@pytest.mark.asyncio` si elle utilise des fonctions asynchrones. Elle doit ensuite créer une application FastAPI, simuler des requêtes et vérifier les réponses.
Comment configurer un fichier de configuration pour Pytest dans un projet FastAPI ?
Pour configurer Pytest, vous pouvez créer un fichier `pytest.ini` à la racine de votre projet. Dans ce fichier, vous pouvez spécifier des options comme le chemin du répertoire des tests, les plugins à utiliser, et d'autres paramètres.

Pages liees

Chaque semaine, le meilleur de la tech francaise

Tendances, salaires, outils et opportunites — directement dans votre boite mail.

Gratuit. Desabonnement en un clic. Pas de spam.