Después de aprender comandos, reportes, configuración y cobertura de ramas, falta una pregunta práctica: cómo mejorar cobertura de manera ordenada sin llenar el proyecto de pruebas frágiles.
En este tema vamos a definir una estrategia de trabajo para decidir dónde probar, qué probar y cuándo conviene refactorizar antes de escribir más pruebas.
Una prueba frágil falla ante cambios que no rompen el comportamiento importante del sistema. Normalmente está demasiado atada a detalles internos.
Señales comunes:
Antes de decidir, genera un reporte limpio.
En Windows PowerShell:
$env:PYTHONPATH="src"
python -m coverage erase
python -m pytest --cov=src --cov-branch --cov-report=term-missing
En Linux o macOS:
python -m coverage erase
PYTHONPATH=src python -m pytest --cov=src --cov-branch --cov-report=term-missing
Trabaja con datos actuales. No tomes decisiones sobre reportes viejos.
No empieces por cualquier línea faltante. Primero identifica módulos importantes con cobertura baja o ramas parciales.
Name Stmts Miss Branch BrPart Cover Missing
---------------------------------------------------------------------
src\tienda\pagos.py 40 16 14 5 59% 12-18, 26, 31-38
src\tienda\carrito.py 25 2 8 1 88% 27, 35
src\tienda\tarifas.py 17 0 12 0 100%
Si pagos.py es crítico, probablemente convenga empezar ahí aunque no sea el único archivo con líneas faltantes.
Abre el archivo señalado por el reporte y entiende qué comportamiento representa cada línea faltante.
Clasifica las líneas faltantes:
Evita pruebas que solo existen para ejecutar una línea. Es mejor nombrar el comportamiento:
def test_autorizar_pago_tarjeta_monto_alto_requiere_revision():
assert autorizar_pago(150000, "tarjeta", 0) == "revision"
El nombre de la prueba explica la regla. Si esa regla se rompe, la falla será fácil de interpretar.
Una prueba como esta sube cobertura, pero aporta poca confianza:
def test_pago_debil():
resultado = autorizar_pago(150000, "tarjeta", 0)
assert resultado is not None
Mejor verifica el resultado esperado:
def test_pago_monto_alto_va_a_revision():
assert autorizar_pago(150000, "tarjeta", 0) == "revision"
No hace falta probar todos los valores posibles. Elige casos representativos:
La parametrización ayuda cuando esos casos comparten estructura.
Si una función es muy difícil de probar, puede ser una señal de diseño. Tal vez mezcla cálculo, entrada de usuario, red, archivos o impresión por pantalla.
Antes:
def procesar_compra():
total = float(input("Total: "))
descuento = total * 0.10
print(f"Total final: {total - descuento}")
Después:
def calcular_total_final(total):
return total - total * 0.10
def procesar_compra():
total = float(input("Total: "))
print(f"Total final: {calcular_total_final(total)}")
Ahora la lógica importante puede probarse como función pura.
Después de agregar pruebas o refactorizar, vuelve a ejecutar:
python -m pytest --cov=src --cov-branch --cov-report=term-missing
Revisa dos cosas:
No todo hueco de cobertura se arregla con una prueba nueva.
pragma: no cover.Una estrategia realista para un proyecto existente:
En este tema organizamos una estrategia para mejorar cobertura sin degradar la calidad de las pruebas. La clave es pasar del reporte a comportamientos verificables, priorizando módulos críticos y evitando pruebas frágiles.
En el próximo tema vamos a cerrar el curso con un caso práctico integrador.