En el tema anterior aprendimos a usar marcadores de pytest. Ahora vamos a aplicar esos marcadores con un criterio práctico: separar pruebas rápidas, lentas, críticas y de regresión.
Esta separación permite elegir qué ejecutar durante el desarrollo, antes de cerrar un cambio o cuando necesitamos una verificación más completa.
No todas las pruebas tienen el mismo costo ni el mismo valor. Algunas se ejecutan en milisegundos y conviene correrlas todo el tiempo. Otras tardan más, dependen de más datos o verifican flujos más amplios.
Separar pruebas ayuda a:
Una prueba rápida se ejecuta en poco tiempo, no depende de servicios externos y prepara pocos datos. Suele validar una función, una regla o un comportamiento pequeño.
Ejemplo:
def test_validar_cupon_con_codigo_correcto_devuelve_true():
assert validar_cupon("DESC10") is True
En muchos proyectos no hace falta marcar las pruebas rápidas. Podemos considerar rápidas a todas las que no estén marcadas como lento.
Una prueba lenta tarda más que el promedio de la suite. Puede ser lenta porque procesa muchos datos, usa archivos grandes, espera algún tiempo o ejecuta un flujo más completo.
Ejemplo:
import time
import pytest
@pytest.mark.lento
def test_generar_reporte_grande_de_ejemplo():
time.sleep(1)
assert True
La marca lento permite excluirla cuando necesitamos una ejecución rápida.
Una prueba crítica valida un comportamiento que no debería romperse nunca. Si falla, el sistema tiene un problema importante.
Ejemplo:
import pytest
@pytest.mark.critica
def test_calcular_total_con_varios_productos_devuelve_suma_total():
productos = [
{"precio": 100, "cantidad": 2},
{"precio": 50, "cantidad": 3},
]
assert calcular_total(productos) == 350
No todas las pruebas deben ser críticas. Si todo es crítico, la etiqueta deja de aportar información.
Una prueba de regresión protege un comportamiento que ya funcionaba y que queremos evitar que vuelva a romperse. Muchas veces nace después de corregir un error.
Ejemplo:
import pytest
@pytest.mark.regresion
def test_normalizar_texto_con_espacios_multiples_devuelve_un_solo_espacio():
assert normalizar_texto("curso de pruebas") == "curso de pruebas"
Las pruebas de regresión son especialmente útiles para errores que ya ocurrieron y podrían repetirse.
En pytest.ini, asegúrate de tener estos marcadores:
markers =
lento: pruebas que tardan más tiempo en ejecutarse
regresion: pruebas importantes para detectar regresiones
critica: pruebas indispensables para validar el comportamiento principal
carrito: pruebas relacionadas con el carrito de compras
texto: pruebas relacionadas con normalización y manejo de textos
También es recomendable mantener --strict-markers en addopts para evitar errores de escritura.
Si consideramos rápidas a todas las pruebas que no están marcadas como lentas, ejecutamos:
python -m pytest -m "not lento"
Este comando es muy útil durante el desarrollo diario.
Para ejecutar solo las pruebas lentas:
python -m pytest -m lento
Esto permite revisar el grupo costoso en momentos específicos, sin mezclarlo con la ejecución rápida.
Para ejecutar solo pruebas críticas:
python -m pytest -m critica
Este grupo debería ejecutarse con frecuencia, porque protege comportamientos fundamentales.
Para ejecutar pruebas de regresión:
python -m pytest -m regresion
También puedes excluir las lentas si hay regresiones más costosas:
python -m pytest -m "regresion and not lento"
Podemos ejecutar pruebas críticas que no sean lentas:
python -m pytest -m "critica and not lento"
También podemos ejecutar todo lo crítico y todo lo de regresión:
python -m pytest -m "critica or regresion"
Estas combinaciones permiten adaptar la ejecución al momento del trabajo.
Una prueba puede ser crítica y de regresión al mismo tiempo:
import pytest
@pytest.mark.critica
@pytest.mark.regresion
def test_calcular_total_con_varios_productos_devuelve_suma_total():
productos = [
{"precio": 100, "cantidad": 2},
{"precio": 50, "cantidad": 3},
]
assert calcular_total(productos) == 350
Esto es correcto si ambas categorías aportan información útil.
Para practicar, crea tests/test_procesos_lentos.py:
import time
import pytest
@pytest.mark.lento
def test_proceso_lento_de_ejemplo_finaliza_correctamente():
time.sleep(1)
assert True
Esta prueba es solo demostrativa. En un proyecto real, una prueba lenta debería tener una razón concreta.
Podemos agregar un argumento --tipo al script:
parser.add_argument(
"--tipo",
choices=["rapida", "lenta", "critica", "regresion"],
help="Ejecuta una suite predefinida",
)
Luego, en construir_comando:
if args.tipo == "rapida":
comando.extend(["-m", "not lento"])
elif args.tipo == "lenta":
comando.extend(["-m", "lento"])
elif args.tipo == "critica":
comando.extend(["-m", "critica"])
elif args.tipo == "regresion":
comando.extend(["-m", "regresion"])
Con el argumento --tipo, puedes ejecutar:
python run_tests.py --tipo rapida
python run_tests.py --tipo lenta
python run_tests.py --tipo critica
python run_tests.py --tipo regresion
Esto hace que la ejecución sea más expresiva para quien no recuerda la sintaxis exacta de -m.
Antes de marcar una prueba, revisa estos criterios:
Una mala clasificación puede causar problemas. Por ejemplo, si marcas demasiadas pruebas como lentas, la suite rápida pierde cobertura. Si marcas demasiadas como críticas, ya no sabes cuáles son verdaderamente indispensables.
La clasificación debe revisarse cuando la suite crece. No es una decisión definitiva para siempre.
Agrega al README.md una sección como esta:
## Tipos de ejecución
Suite rápida:
python -m pytest -m "not lento"
Pruebas lentas:
python -m pytest -m lento
Pruebas críticas:
python -m pytest -m critica
Pruebas de regresión:
python -m pytest -m regresion
Documentar la estrategia evita que cada persona use criterios diferentes.
lento.regresion.leer_argumentos.Clasifica al menos cinco pruebas del proyecto:
Luego ejecuta:
python -m pytest -m "not lento"
python -m pytest -m critica
python -m pytest -m regresion
python -m pytest -m lento
Ejemplo de prueba crítica y de regresión:
import pytest
from app.carrito import calcular_total
@pytest.mark.critica
@pytest.mark.regresion
def test_calcular_total_con_varios_productos_devuelve_suma_total():
productos = [
{"precio": 100, "cantidad": 2},
{"precio": 50, "cantidad": 3},
]
assert calcular_total(productos) == 350
Ejemplo de prueba lenta:
import time
import pytest
@pytest.mark.lento
def test_proceso_lento_de_ejemplo_finaliza_correctamente():
time.sleep(1)
assert True
Antes de continuar con el próximo tema, verifica lo siguiente:
@pytest.mark.lento.@pytest.mark.critica.@pytest.mark.regresion.python -m pytest -m "not lento".--tipo.En este tema separamos la suite por velocidad y riesgo. Esta clasificación permite elegir la ejecución adecuada para cada momento: rápida durante el desarrollo, crítica antes de entregar, regresión para prevenir errores conocidos y lenta cuando se necesita una revisión más completa.
En el próximo tema trabajaremos con fixtures reutilizables para preparar datos, objetos y estados iniciales de forma ordenada.