La consola es útil para el trabajo diario, pero muchas veces necesitamos conservar evidencia de una ejecución o compartir resultados con otra herramienta.
En este tema generaremos reportes HTML para lectura humana y archivos JUnit XML para integración con herramientas externas.
Usaremos dos formatos:
Ambos pueden generarse desde pytest.
Para generar HTML necesitamos pytest-html. Instálalo si hace falta:
python -m pip install pytest-html
Luego puedes ejecutar:
python -m pytest --version
Si la instalación fue correcta, pytest aceptará la opción --html.
Si todavía no existe, crea la carpeta de reportes:
mkdir reports
En los scripts de Python también podemos crearla automáticamente con Path("reports").mkdir(exist_ok=True).
Ejecuta:
python -m pytest --html=reports/reporte.html --self-contained-html
La opción --self-contained-html genera un HTML autocontenido, más fácil de compartir porque incluye estilos dentro del mismo archivo.
Después de ejecutar el comando, abre reports/reporte.html en el navegador.
El reporte muestra información como:
pytest puede generar JUnit XML sin instalar extensiones adicionales:
python -m pytest --junitxml=reports/junit.xml
El archivo XML no está pensado principalmente para lectura humana. Sirve para que otras herramientas puedan procesar resultados.
Podemos generar ambos reportes en una sola ejecución:
python -m pytest --html=reports/reporte.html --self-contained-html --junitxml=reports/junit.xml
Esto permite tener evidencia visual y un archivo estructurado para herramientas.
Los reportes también pueden generarse para una parte de la suite:
python -m pytest tests/test_carrito.py --html=reports/carrito.html --self-contained-html
Esto es útil cuando quieres evidencia de una funcionalidad concreta.
También puedes generar reportes para pruebas marcadas:
python -m pytest -m critica --html=reports/criticas.html --self-contained-html
El nombre del archivo debería reflejar qué grupo de pruebas se ejecutó.
Para no sobrescribir reportes anteriores, podemos usar una marca de tiempo en el nombre. En Python:
from datetime import datetime
def crear_nombre_reporte():
marca = datetime.now().strftime("%Y%m%d_%H%M%S")
return f"reporte_{marca}.html"
Esto genera nombres como reporte_20260510_153000.html.
En run_tests.py, asegúrate de tener una carpeta de reportes:
from pathlib import Path
REPORTS_DIR = Path("reports")
Y al construir el comando:
if args.reporte:
REPORTS_DIR.mkdir(exist_ok=True)
comando.extend([
"--html=reports/reporte.html",
"--self-contained-html",
])
Agrega un argumento:
parser.add_argument("--junit", action="store_true", help="Genera reporte JUnit XML")
Y en construir_comando:
if args.junit:
REPORTS_DIR.mkdir(exist_ok=True)
comando.append("--junitxml=reports/junit.xml")
Uso:
python run_tests.py --junit
Con las opciones anteriores puedes ejecutar:
python run_tests.py --reporte --junit
Esto debería generar:
reports/reporte.html
reports/junit.xml
Generar reportes no significa que la ejecución haya sido exitosa. El script debe seguir devolviendo el código de salida de pytest.
resultado = subprocess.run(comando, check=False)
return resultado.returncode
Si una prueba falla, el reporte se genera, pero el código de salida debe indicar fallo.
Normalmente los reportes generados no se suben al repositorio. Agrega en .gitignore:
reports/
La carpeta puede recrearse en cada ejecución. Lo importante es conservar los comandos que generan los reportes, no los archivos generados.
Podemos agregar una opción para limpiar reportes antes de ejecutar:
import shutil
def limpiar_reportes():
if REPORTS_DIR.exists():
shutil.rmtree(REPORTS_DIR)
REPORTS_DIR.mkdir()
Úsalo con cuidado. No limpies reportes si necesitas conservar evidencia histórica.
Argumento:
parser.add_argument("--limpiar-reportes", action="store_true", help="Elimina reportes previos antes de ejecutar")
Uso en main antes de ejecutar pytest:
if args.limpiar_reportes:
limpiar_reportes()
Comando:
python run_tests.py --limpiar-reportes --reporte --junit
Si usas pytest-xdist, también puedes generar reportes:
python -m pytest -n auto --html=reports/reporte.html --self-contained-html --junitxml=reports/junit.xml
Si aparece un problema extraño, ejecuta primero sin -n auto para distinguir si el problema es de paralelización o del reporte.
reports.pytest-html.reports o que el script la cree.Actualiza run_tests.py para soportar:
--reporte para HTML.--junit para XML.--limpiar-reportes para borrar reportes previos.Luego ejecuta:
python run_tests.py --limpiar-reportes --reporte --junit
Fragmentos principales:
import shutil
from pathlib import Path
REPORTS_DIR = Path("reports")
def limpiar_reportes():
if REPORTS_DIR.exists():
shutil.rmtree(REPORTS_DIR)
REPORTS_DIR.mkdir()
parser.add_argument("--junit", action="store_true", help="Genera reporte JUnit XML")
parser.add_argument("--limpiar-reportes", action="store_true", help="Elimina reportes previos antes de ejecutar")
En la construcción del comando:
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")
Antes de continuar con el próximo tema, verifica lo siguiente:
pytest-html está instalado.reports/reporte.html.reports/junit.xml.reports/ está en .gitignore si corresponde.run_tests.py.En este tema generamos reportes HTML y JUnit XML. El HTML ayuda a revisar resultados visualmente y el XML permite que otras herramientas procesen la ejecución.
En el próximo tema automatizaremos tareas repetitivas con scripts de Python para seguir simplificando el trabajo diario con la suite.