Pruebas de FastAPI

FastAPI, basado en Starlette,TestClientproporciona un soporte de pruebas conveniente que permite probar las solicitudes y respuestas de la API sin iniciar el servidor.


Instalar dependencias

TestClientBasado enhttpx, es necesario instalar:

pip install httpx

Pruebas básicas

UsaTestClientpara crear un cliente de prueba y luego, igual que conhttpx, envía solicitudes:

Ejemplo

from fastapi import FastAPI
from fastapi.testclient import TestClient

app = FastAPI()


@app.get("/")
async def root():
    return {"message": "Hello World"}


# Crear el cliente de prueba
client = TestClient(app)


# Función de prueba
def test_root():
    response = client.get("/")
    assert response.status_code == 200
    assert response.json() == {"message": "Hello World"}

TestClientLlama directamente a la aplicación FastAPI sin pasar por la capa de red, por lo que las pruebas son muy rápidas. No necesitas iniciar el servidor Uvicorn.


Probar varios tipos de solicitudes

Ejemplo

from fastapi import FastAPI
from fastapi.testclient import TestClient
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    price: float


@app.post("/items/")
async def create_item(item: Item):
    return {"name": item.name, "price": item.price}


@app.get("/items/{item_id}")
async def read_item(item_id: int):
    return {"item_id": item_id}


@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
    return {"item_id": item_id, "name": item.name}


@app.delete("/items/{item_id}")
async def delete_item(item_id: int):
    return {"deleted": item_id}


client = TestClient(app)


# Probar solicitud POST
def test_create_item():
    response = client.post(
        "/items/",
        json={"name": "Foo", "price": 42.0},  # Enviar cuerpo de solicitud JSON
    )
    assert response.status_code == 200
    assert response.json() == {"name": "Foo", "price": 42.0}


# Probar solicitud GET
def test_read_item():
    response = client.get("/items/1")
    assert response.status_code == 200
    assert response.json() == {"item_id": 1}


# Probar solicitud PUT
def test_update_item():
    response = client.put(
        "/items/1",
        json={"name": "Bar", "price": 50.0},
    )
    assert response.status_code == 200


# Probar solicitud DELETE
def test_delete_item():
    response = client.delete("/items/1")
    assert response.status_code == 200
    assert response.json() == {"deleted": 1}

Probar parámetros de consulta y encabezados de solicitud

Ejemplo

from typing import Annotated
from fastapi import FastAPI, Header
from fastapi.testclient import TestClient

app = FastAPI()


@app.get("/items/")
async def read_items(
    skip: int = 0,
    limit: int = 10,
    x_token: Annotated[str | None, Header()] = None,
):
    return {"skip": skip, "limit": limit, "x_token": x_token}


client = TestClient(app)


# Probar parámetros de consulta
def test_read_items_with_params():
    response = client.get("/items/?skip=5&limit=20")
    assert response.status_code == 200
    assert response.json() == {"skip": 5, "limit": 20, "x_token": None}


# Probar encabezados de solicitud
def test_read_items_with_header():
    response = client.get(
        "/items/",
        headers={"X-Token": "fake-token"},  # Enviar encabezados de solicitud personalizados
    )
    assert response.status_code == 200
    assert response.json()["x_token"] == "fake-token"

Probar respuestas de error

Ejemplo

from fastapi import FastAPI, HTTPException
from fastapi.testclient import TestClient

app = FastAPI()


@app.get("/items/{item_id}")
async def read_item(item_id: int):
    if item_id == 0:
        raise HTTPException(status_code=404, detail="Item not found")
    return {"item_id": item_id}


client = TestClient(app)


# Probar respuesta normal
def test_read_item():
    response = client.get("/items/1")
    assert response.status_code == 200


# Probar respuesta de error
def test_read_item_not_found():
    response = client.get("/items/0")
    assert response.status_code == 404
    assert response.json() == {"detail": "Item not found"}


# Probar errores de validación
def test_read_item_invalid_id():
    response = client.get("/items/foo")
    assert response.status_code == 422  # Validación fallida

Ejecutar pruebas con pytest

Guarda el código de prueba comotest_main.py, y luego ejecuta:

$ pytest test_main.py

# 显示详细输出
$ pytest test_main.py -v

# 显示 print 输出
$ pytest test_main.py -s

pytest descubre automáticamentetest_los archivos y funciones que comienzan con el prefijo.


Sobrescribir dependencias en las pruebas

En las pruebas, es posible que necesites sobrescribir ciertas dependencias (como la conexión a la base de datos o la lógica de autenticación):

Ejemplo

from fastapi import Depends, FastAPI
from fastapi.testclient import TestClient

app = FastAPI()


# Función de dependencia real
def get_query_param():
    return "real_value"


@app.get("/items/")
async def read_items(query: str = Depends(get_query_param)):
    return {"query": query}


client = TestClient(app)


def test_with_override():
    # Sobrescribir la dependencia
    app.dependency_overrides[get_query_param] = lambda: "test_value"

    response = client.get("/items/")
    assert response.json() == {"query": "test_value"}

    # Eliminar la sobrescritura
    app.dependency_overrides.clear()

app.dependency_overridesTe permite reemplazar cualquier función de dependencia en las pruebas, ideal para simular escenarios como conexiones de base de datos o llamadas a API externas. Recuerda eliminar la sobrescritura al finalizar las pruebas.


Resumen

  • UsarTestClientpara probar directamente la aplicación FastAPI sin iniciar el servidor
  • Admite pruebas de todos los métodos HTTP, como GET, POST, PUT, DELETE, etc.
  • Puede probar parámetros de consulta, encabezados de solicitud, cuerpo de solicitud, respuestas de error, etc.
  • Usarapp.dependency_overridespara sobrescribir dependencias en las pruebas
  • Junto con pytest, se pueden organizar y ejecutar pruebas fácilmente.
Otras extensiones