Contenido de la lección 9 tramos
Async, docs y testing
¿Qué es?
Este módulo reúne tres habilidades finales. Async (asíncrono) es una forma de programar para atender muchas peticiones a la vez mientras unas esperan respuestas externas. Docs es la documentación de tu API: la página interactiva que FastAPI genera sola para que otros entiendan y prueben tus endpoints. Testing es escribir pruebas automáticas que verifican que tu API se comporta como esperas, sin que tengas que revisarlo todo a mano.
¿Cómo funciona?
Con async def y await, un endpoint puede ceder el control mientras espera una operación de entrada/salida, y el servidor aprovecha para atender otras peticiones; para trabajo posterior a la respuesta existen las BackgroundTasks. La documentación se enriquece con metadatos como title, summary y tags, y se alimenta sola de tus tipos y modelos. Las pruebas usan TestClient, que llama a tu app sin levantar un servidor real, y comprueban con assert que cada respuesta tenga el estado y el contenido correctos.
¿Para qué sirve?
Estas tres piezas separan una API de juguete de una lista para usarse: la asincronía la hace rendir bajo carga, la documentación la vuelve usable por otros, y las pruebas avisan cuando algo se rompe. Juntas te dan confianza de que el sistema funciona y de que seguirá funcionando al cambiarlo. Con ellas entregarás tu proyecto final, «API para tus herramientas», sabiendo que corre de forma concurrente, se documenta sola y se prueba automáticamente.
Cierras el curso con tres habilidades que separan una API de juguete de una lista para usarse: ejecutar trabajo de forma asíncrona, documentar profesionalmente y probar automáticamente. Con esto entregarás tu «API para tus herramientas» con confianza de que funciona y de que otros sabrán usarla.
async/await: cuándo y por qué
FastAPI es asíncrono de raíz. Un endpoint puede ser normal (def) o asíncrono (async def). La diferencia importa cuando esperas operaciones de entrada/salida: una llamada HTTP a otro servicio, una consulta a base de datos asíncrona, leer un archivo grande.
import httpx
from fastapi import FastAPI
app = FastAPI()
@app.get("/clima/{ciudad}")
async def clima(ciudad: str):
async with httpx.AsyncClient() as client:
resp = await client.get(f"https://api.ejemplo.com/clima/{ciudad}")
return resp.json()
Mientras una petición espera la respuesta externa (await), el servidor atiende otras. Eso es concurrencia: más peticiones simultáneas con los mismos recursos. La regla práctica: usa async def cuando dentro hagas await; usa def normal para código que no espera nada o que usa librerías síncronas.
Nunca pongas trabajo bloqueante dentro de un async def. Un time.sleep(5) o un cálculo pesado dentro de una corrutina congela todo el servidor para todos. Si tu función no usa await, decláralala con def normal: FastAPI la corre en un hilo aparte y no bloquea el bucle de eventos.
Para tareas que deben ocurrir después de responder (enviar un correo, escribir un log), usa BackgroundTasks:
from fastapi import BackgroundTasks
def registrar(mensaje: str):
with open("log.txt", "a") as f:
f.write(mensaje + "\n")
@app.post("/notas")
async def crear(valor: float, tareas: BackgroundTasks):
tareas.add_task(registrar, f"Nota creada: {valor}")
return {"valor": valor}
El cliente recibe la respuesta de inmediato y el log se escribe en segundo plano.
Documentación profesional
FastAPI genera /docs (Swagger UI) y /redoc solo, a partir de tus tipos. Tú la enriqueces con metadatos:
app = FastAPI(
title="API para tus herramientas",
description="Expone un calificador OMR y un sistema de notas.",
version="1.0.0",
)
@app.post("/notas", summary="Registrar una nota", tags=["notas"])
def crear_nota(valor: float):
"""
Crea una nota nueva.
- **valor**: número de 0.0 a 5.0
"""
return {"valor": valor}
El summary y el docstring aparecen en /docs; los tags agrupan endpoints por sección. Documentar respuestas de error también ayuda:
@app.get(
"/notas/{nota_id}",
responses={404: {"description": "Nota no encontrada"}},
)
def obtener(nota_id: int):
...
La mejor documentación es la que se escribe sola. Cada modelo Pydantic, cada response_model y cada tipo que declaras ya alimenta /docs. Invertir en buenos modelos te ahorra mantener documentación aparte que se desactualiza.
Testing con TestClient
FastAPI incluye un cliente de pruebas que llama a tu app sin levantar un servidor real. Se apoya en pytest:
pip install pytest httpx
Supongamos main.py con la app. Creas test_main.py:
from fastapi.testclient import TestClient
from main import app
client = TestClient(app)
def test_crear_nota():
resp = client.post("/notas", json={"estudiante": "Ana", "valor": 4.5})
assert resp.status_code == 201
assert resp.json()["aprobada"] is True
def test_valor_invalido():
resp = client.post("/notas", json={"estudiante": "Ana", "valor": 9})
assert resp.status_code == 422
def test_nota_inexistente():
resp = client.get("/notas/9999")
assert resp.status_code == 404
Cada prueba envía una petición y verifica el resultado con assert. Corres todo con pytest. Fíjate en qué probamos: el camino feliz (201), la validación (422) y los errores (404). Una buena API se prueba por su comportamiento visible, no por su código interno.
Ejemplo guiado: del endpoint a la prueba
- Toma el
POST /notasconresponse_modeldel módulo de Pydantic enmain.py. - Crea
test_main.pycon las tres pruebas de arriba. - Corre
pytest -v. Verás tres líneas en verde. - Rompe a propósito el rango del modelo (pon
le=10) y vuelve a correr:test_valor_invalidofalla, porque ahora9se acepta. Eso es una prueba haciendo su trabajo: avisar cuando el comportamiento cambia. - Repara el modelo y confirma que todo vuelve a verde.
Para probar endpoints protegidos, primero pides un token y lo mandas en la cabecera:
def test_crear_requiere_auth():
assert client.post("/notas", json={"valor": 4.0}).status_code == 401
login = client.post("/login", data={"username": "profe", "password": "clave123"})
token = login.json()["access_token"]
headers = {"Authorization": f"Bearer {token}"}
assert client.post("/notas", json={"valor": 4.0}, headers=headers).status_code == 201
Tech English: concurrency = concurrencia (atender varias cosas a la vez). blocking = bloqueante. fixture = preparación reutilizable de una prueba en pytest. assertion = afirmación que la prueba verifica. coverage = cobertura del código probado.
Ejercicios
-
Suite de pruebas del CRUD. Escribe pruebas con
TestClientpara tu API de notas que cubran: crear (201), leer existente (200), leer inexistente (404), validación fallida (422) y borrar (204). Evalúa: ¿cada prueba comprueba código de estado y contenido relevante? ¿se cubren los caminos de error, no solo el feliz? -
Calificador OMR como API documentada y probada. Diseña
POST /calificarque reciba una hoja OMR y la clave de respuestas, y devuelva el puntaje. Documenta el endpoint consummary,tagsy descripción de la respuesta 422; añade pruebas para una hoja perfecta, una a medias y una con una letra inválida. Evalúa el diseño completo: recursos claros, validación en el modelo, errores útiles y pruebas que defienden el comportamiento. Este es el corazón del proyecto final, «API para tus herramientas».
Fin de la lección: abajo están las preguntas y los comentarios.
¿Le sirvió esta lección?
Sirve para saber qué reescribir. Se puede cambiar cuando quiera.
Su avance y su nombre se guardan en este navegador. No hace falta registrarse.
¿Va por el certificado?
Practicar y comentar no exige nada. Para el certificado sí hace falta verificar el correo, el nombre completo, la institución, el grado, porque el certificado dice quién es usted.
Le llega un código de 6 dígitos. No hay contraseña que recordar.
Vence en 15 minutos.
Comentarios (0)
Pregunte lo que no entendió. Alguien más tiene la misma duda y no se atreve.
- Todavía no hay comentarios. Estrene usted.