En temas anteriores agregamos varias opciones a run_tests.py. Cuando un script tiene muchas opciones, puede volverse difícil recordar qué combinación usar para cada situación.
En este tema crearemos comandos simples para ejecutar suites por objetivo: rápida, crítica, regresión, lenta, con reporte, en paralelo o de diagnóstico.
Ejecutar por objetivo significa elegir una intención antes que una lista de opciones técnicas. Por ejemplo:
rapida: ejecutar todo excepto pruebas lentas.critica: ejecutar pruebas indispensables.regresion: ejecutar pruebas de regresión.completa: ejecutar toda la suite.diagnostico: ejecutar con más información.El usuario del script no necesita recordar todos los flags internos de pytest.
Queremos poder ejecutar comandos como estos:
python run_tests.py rapida
python run_tests.py critica
python run_tests.py regresion
python run_tests.py completa
python run_tests.py diagnostico
Cada objetivo construirá el comando de pytest correspondiente.
Actualiza run_tests.py para aceptar un objetivo posicional:
import argparse
import subprocess
import sys
from pathlib import Path
REPORTS_DIR = Path("reports")
def leer_argumentos():
parser = argparse.ArgumentParser(description="Ejecuta suites de pruebas por objetivo")
parser.add_argument(
"objetivo",
nargs="?",
default="completa",
choices=["completa", "rapida", "critica", "regresion", "lenta", "diagnostico"],
help="Objetivo de ejecución",
)
return parser.parse_args()
Si no se indica objetivo, usaremos completa.
Crea una función para traducir objetivos a opciones de pytest:
def construir_comando_por_objetivo(objetivo):
comando = [sys.executable, "-m", "pytest"]
if objetivo == "rapida":
comando.extend(["-m", "not lento"])
elif objetivo == "critica":
comando.extend(["-m", "critica"])
elif objetivo == "regresion":
comando.extend(["-m", "regresion"])
elif objetivo == "lenta":
comando.extend(["-m", "lento"])
elif objetivo == "diagnostico":
comando.extend(["-v", "--tb=short", "--durations=5", "-ra"])
return comando
El objetivo completa no agrega filtros.
Agrega la función principal:
def main():
args = leer_argumentos()
comando = construir_comando_por_objetivo(args.objetivo)
print("Ejecutando:", " ".join(comando))
resultado = subprocess.run(comando, check=False)
return resultado.returncode
if __name__ == "__main__":
raise SystemExit(main())
Ahora puedes ejecutar:
python run_tests.py rapida
Podemos conservar opciones como --reporte, --junit y --workers:
parser.add_argument("--reporte", action="store_true", help="Genera reporte HTML")
parser.add_argument("--junit", action="store_true", help="Genera reporte JUnit XML")
parser.add_argument("--workers", help="Cantidad de procesos para pytest-xdist")
Así podemos combinar objetivo y opciones:
python run_tests.py rapida --reporte
python run_tests.py regresion --workers auto
Extiende la construcción del comando:
def agregar_opciones_generales(comando, args):
if args.workers:
comando.extend(["-n", args.workers])
if args.reporte:
REPORTS_DIR.mkdir(exist_ok=True)
comando.extend([
"--html=reports/reporte.html",
"--self-contained-html",
])
if args.junit:
REPORTS_DIR.mkdir(exist_ok=True)
comando.append("--junitxml=reports/junit.xml")
return comando
La función main puede quedar así:
def main():
args = leer_argumentos()
comando = construir_comando_por_objetivo(args.objetivo)
comando = agregar_opciones_generales(comando, args)
print("Ejecutando:", " ".join(comando))
resultado = subprocess.run(comando, check=False)
return resultado.returncode
El script conserva el código de salida real de pytest.
Ejecuta cada objetivo:
python run_tests.py completa
python run_tests.py rapida
python run_tests.py critica
python run_tests.py regresion
python run_tests.py lenta
python run_tests.py diagnostico
Si alguna categoría no tiene pruebas marcadas, pytest puede indicar que no encontró pruebas para esa selección.
argparse genera ayuda automáticamente:
python run_tests.py --help
Es importante que los textos de ayuda expliquen el objetivo de cada opción.
El parámetro choices evita objetivos inválidos:
choices=["completa", "rapida", "critica", "regresion", "lenta", "diagnostico"]
Si alguien escribe un objetivo incorrecto, el script muestra un error claro antes de ejecutar pytest.
Podemos crear un objetivo que genere HTML y XML automáticamente:
choices=[
"completa",
"rapida",
"critica",
"regresion",
"lenta",
"diagnostico",
"reporte",
]
Y en main:
if args.objetivo == "reporte":
args.reporte = True
args.junit = True
Uso:
python run_tests.py reporte
No conviene crear un objetivo para cada combinación posible. Los objetivos deben representar flujos reales de trabajo.
Buenos objetivos iniciales:
completarapidacriticaregresiondiagnosticoreporteLos scripts del tema anterior pueden usar los nuevos objetivos:
pasos = [
[sys.executable, "scripts/verificar_estructura.py"],
[sys.executable, "run_tests.py", "rapida"],
]
Esto hace que la rutina sea más expresiva.
Agrega al README.md:
## Objetivos de ejecución
Suite completa:
python run_tests.py completa
Suite rápida:
python run_tests.py rapida
Pruebas críticas:
python run_tests.py critica
Pruebas de regresión:
python run_tests.py regresion
Diagnóstico:
python run_tests.py diagnostico
Reportes HTML y XML:
python run_tests.py reporte
Un comando simple debe significar siempre lo mismo. Si rapida significa not lento, mantén ese criterio en la documentación, en el script y en la comunicación del equipo.
La automatización pierde valor si cada persona interpreta los objetivos de forma distinta.
Si quieres seguir permitiendo rutas específicas, agrega un argumento opcional:
parser.add_argument("--ruta", help="Archivo o carpeta de pruebas")
Y luego:
if args.ruta:
comando.append(args.ruta)
Uso:
python run_tests.py rapida --ruta tests/test_carrito.py
help.run_tests.py.Actualiza run_tests.py para soportar estos objetivos:
completarapidacriticaregresiondiagnosticoreporteLuego prueba:
python run_tests.py --help
python run_tests.py rapida
python run_tests.py reporte
Fragmento principal:
def construir_comando_por_objetivo(objetivo):
comando = [sys.executable, "-m", "pytest"]
if objetivo == "rapida":
comando.extend(["-m", "not lento"])
elif objetivo == "critica":
comando.extend(["-m", "critica"])
elif objetivo == "regresion":
comando.extend(["-m", "regresion"])
elif objetivo == "diagnostico":
comando.extend(["-v", "--tb=short", "--durations=5", "-ra"])
return comando
Activar reportes desde el objetivo:
if args.objetivo == "reporte":
args.reporte = True
args.junit = True
Antes de continuar con el próximo tema, verifica lo siguiente:
choices.python -m pytest internamente.--help muestra ayuda comprensible.En este tema creamos comandos simples para ejecutar suites por objetivo. Esto hace que la automatización sea más cómoda y reduce la necesidad de recordar combinaciones largas de opciones.
En el próximo tema veremos cómo limpiar automáticamente datos, archivos y recursos después de cada prueba.