Probar una app Flask con pytest desde cero
Cómo escribir pruebas automáticas para tu app Flask con pytest: el cliente de prueba, verificar rutas y respuestas, y el fixture que evita repetir código. Para dejar de probar a mano.
Probar tu app Flask abriendo el navegador y haciendo clic en todo cada vez que cambias algo es lento y, peor, poco fiable: siempre se te escapa un caso. Las pruebas automáticas con pytest verifican tu app en segundos y te avisan si rompiste algo. Aquí cómo empezar desde cero, sin teoría de más.
pip install pytest
El cliente de prueba de Flask
Flask trae un “cliente de prueba” que simula peticiones HTTP a tu app sin levantar un servidor real ni abrir un navegador. Es la pieza central. Supongamos esta app mínima:
# app.py
from flask import Flask
app = Flask(__name__)
@app.route("/")
def inicio():
return "Hola, Pyforja"
@app.route("/saludo/<nombre>")
def saludo(nombre):
return f"Hola, {nombre}"
Tu primera prueba
Crea un archivo test_app.py (el prefijo test_ es importante: así pytest lo encuentra solo):
# test_app.py
from app import app
def test_inicio():
cliente = app.test_client()
respuesta = cliente.get("/")
assert respuesta.status_code == 200
assert b"Hola, Pyforja" in respuesta.data
Tres cosas clave aquí. app.test_client() crea el cliente simulado. cliente.get("/") hace una petición GET a esa ruta, como lo haría un navegador. Y los assert comprueban lo que esperas: que el código sea 200 (OK) y que el texto aparezca en la respuesta.
Fíjate en la b antes del texto: b"Hola, Pyforja". La respuesta viene en bytes, no en texto normal, así que la comparación también debe ser en bytes. Es el tropiezo número uno al empezar con el cliente de prueba — si comparas sin la b, la prueba falla aunque el texto esté bien.
Ejecutar las pruebas
Desde la terminal, en la carpeta del proyecto:
pytest
pytest busca todos los archivos test_*.py, ejecuta cada función que empiece por test_, y te muestra un resumen: verde si pasan, rojo con el detalle si alguna falla. Un punto por cada prueba que pasa.
Probar rutas con parámetros
def test_saludo():
cliente = app.test_client()
respuesta = cliente.get("/saludo/Martin")
assert respuesta.status_code == 200
assert b"Hola, Martin" in respuesta.data
El fixture: dejar de repetir el cliente
Habrás notado que en cada prueba repites cliente = app.test_client(). pytest tiene fixtures: funciones que preparan algo una vez y lo entregan a cada prueba que lo pida. Para el cliente:
import pytest
from app import app
@pytest.fixture
def cliente():
return app.test_client()
def test_inicio(cliente):
respuesta = cliente.get("/")
assert respuesta.status_code == 200
def test_saludo(cliente):
respuesta = cliente.get("/saludo/Ana")
assert b"Hola, Ana" in respuesta.data
El truco: la función decorada con @pytest.fixture se llama cliente, y cada prueba que ponga cliente como argumento recibe automáticamente lo que esa función devuelve. Menos repetición, y si cambia cómo creas el cliente, lo cambias en un solo sitio.
Probar una ruta que no existe (el 404)
Las pruebas no son solo para lo que funciona; también para confirmar que lo que debe fallar, falla bien:
def test_ruta_inexistente(cliente):
respuesta = cliente.get("/no-existe")
assert respuesta.status_code == 404
Comprobar los casos de error es tan importante como los de éxito. Una app que devuelve 200 donde debería dar 404 tiene un bug silencioso.
Resumen
app.test_client()simula peticiones sin navegador ni servidor.- Las respuestas vienen en bytes: compara con
b"texto". - Archivos
test_*.pyy funcionestest_*para que pytest las encuentre. - Usa un
@pytest.fixturepara no repetir la creación del cliente. - Prueba también los casos de error (404, 400), no solo los de éxito.
Con esto, cada vez que cambies algo en tu app corres pytest y en segundos sabes si todo sigue funcionando. Es la red de seguridad que te deja hacer cambios sin miedo a romper algo sin darte cuenta.
¿Necesitas probar rutas que requieren login, o una base de datos de prueba aislada? Escríbeme desde contacto y lo cubrimos.