20. Cobertura por módulo: priorizar zonas críticas del sistema

20.1 Objetivo del tema

Un porcentaje total de cobertura puede ocultar diferencias importantes entre módulos. Un proyecto puede tener buen promedio general y, al mismo tiempo, tener poca cobertura en una parte crítica.

En este tema vamos a leer el reporte por archivo y priorizar dónde agregar pruebas según riesgo, importancia y cantidad de código sin cubrir.

Objetivo práctico: usar la cobertura por módulo para decidir qué zona del sistema conviene mejorar primero.

20.2 Generar el reporte por archivo

Ejecuta el reporte con líneas faltantes y cobertura de ramas.

En Windows PowerShell:

$env:PYTHONPATH="src"
python -m pytest --cov=src --cov-branch --cov-report=term-missing

En Linux o macOS:

PYTHONPATH=src python -m pytest --cov=src --cov-branch --cov-report=term-missing

Una salida posible es:

Name                      Stmts   Miss Branch BrPart  Cover   Missing
---------------------------------------------------------------------
src\tienda\carrito.py        25      2      8      1    88%   27, 35
src\tienda\cupones.py        22      0     10      0   100%
src\tienda\pagos.py          40     16     14      5    59%   12-18, 26, 31-38
src\tienda\tarifas.py        17      0     12      0   100%
---------------------------------------------------------------------
TOTAL                       104     18     44      6    80%

El total es útil, pero la decisión importante está en las filas por archivo.

20.3 El problema del promedio global

En el ejemplo, el total es 80%. Ese número puede parecer aceptable, pero src\tienda\pagos.py está en 59%.

Si pagos.py contiene reglas de cobro, validaciones de importes o decisiones de aprobación, probablemente sea más urgente que un módulo auxiliar con porcentaje menor pero poco riesgo.

20.4 Priorizar por riesgo

No todos los módulos tienen la misma importancia. Para priorizar, considera:

  • Impacto económico: pagos, facturación, descuentos y saldos.
  • Impacto de seguridad: permisos, autenticación y autorización.
  • Frecuencia de cambio: módulos que se modifican seguido.
  • Complejidad: muchas ramas, estados o reglas combinadas.
  • Historial de errores: zonas donde ya aparecieron defectos.

20.5 Crear una tabla de decisión

Una forma práctica es combinar cobertura y criticidad:

Módulo                 Cobertura   Criticidad   Prioridad
---------------------------------------------------------
pagos.py                  59%      alta         alta
carrito.py                88%      media        media
cupones.py               100%      media        baja
tarifas.py               100%      baja         baja

Esta tabla evita decidir solo por porcentaje. Un módulo crítico al 75% puede merecer atención antes que un módulo poco usado al 40%.

20.6 Analizar un módulo crítico

Supongamos que pagos.py contiene:

def autorizar_pago(monto, metodo, intentos_fallidos):
    if monto <= 0:
        raise ValueError("El monto debe ser mayor que cero")

    if intentos_fallidos >= 3:
        return "bloqueado"

    if metodo == "tarjeta":
        if monto > 100000:
            return "revision"
        return "aprobado"

    if metodo == "transferencia":
        return "pendiente"

    return "rechazado"

Un reporte con líneas faltantes en este módulo merece atención porque cada rama representa una decisión importante.

20.7 Agregar pruebas por comportamiento

Para mejorar cobertura de un módulo crítico, no empieces por líneas sueltas. Agrupa las pruebas por comportamiento:

import pytest

from tienda.pagos import autorizar_pago


def test_autorizar_pago_rechaza_monto_invalido():
    with pytest.raises(ValueError):
        autorizar_pago(0, "tarjeta", 0)


def test_autorizar_pago_bloquea_por_intentos_fallidos():
    assert autorizar_pago(1000, "tarjeta", 3) == "bloqueado"


def test_autorizar_pago_tarjeta_monto_alto_requiere_revision():
    assert autorizar_pago(150000, "tarjeta", 0) == "revision"

Estas pruebas suben cobertura y, más importante, verifican reglas relevantes.

20.8 Completar caminos relevantes

Luego agrega casos para los demás caminos de negocio:

def test_autorizar_pago_tarjeta_monto_normal_aprueba():
    assert autorizar_pago(5000, "tarjeta", 0) == "aprobado"


def test_autorizar_pago_transferencia_queda_pendiente():
    assert autorizar_pago(5000, "transferencia", 0) == "pendiente"


def test_autorizar_pago_metodo_desconocido_rechaza():
    assert autorizar_pago(5000, "cripto", 0) == "rechazado"

El reporte debería mejorar, pero el criterio principal sigue siendo cubrir decisiones reales del módulo.

20.9 Medir solo un módulo durante el trabajo

Mientras trabajas sobre un módulo específico, puedes filtrar el reporte visualmente o ejecutar pruebas enfocadas. Por ejemplo:

python -m pytest tests/test_pagos.py --cov=src --cov-branch --cov-report=term-missing

Esto no cambia el criterio final del proyecto, pero acelera el ciclo mientras mejoras una zona concreta.

20.10 Usar reporte HTML para priorizar

El reporte HTML ayuda a inspeccionar un módulo crítico con más contexto:

python -m pytest --cov=src --cov-branch --cov-report=html

Abre htmlcov/index.html y entra al archivo con baja cobertura. Revisa qué líneas faltan y qué ramas parciales aparecen.

20.11 No perseguir módulos irrelevantes primero

Un archivo pequeño de utilidades internas puede tener baja cobertura, pero si casi no se usa o no contiene reglas importantes, quizá no sea la primera prioridad.

La cobertura debe orientar el trabajo. No debe reemplazar una decisión técnica sobre riesgo, impacto y valor de las pruebas.

20.12 Registrar decisiones

Cuando un equipo decide priorizar ciertos módulos, conviene dejarlo explícito. Por ejemplo:

  • Pagos: mantener cobertura alta por criticidad económica.
  • Cupones: cubrir reglas de descuentos y límites.
  • Scripts manuales: fuera del objetivo de cobertura principal.
  • Integraciones externas: cubrir adaptadores con pruebas específicas o dobles de prueba.

20.13 Señales de alerta

  • Módulo crítico por debajo del promedio: revisar primero.
  • Muchas ramas parciales: faltan caminos alternativos.
  • Líneas faltantes en validaciones: faltan pruebas de errores o casos borde.
  • Módulo con cambios frecuentes y baja cobertura: alto riesgo de regresiones.

20.14 Errores frecuentes

  • Mirar solo el TOTAL: puede ocultar archivos críticos con poca cobertura.
  • Atacar el porcentaje más bajo sin contexto: prioriza por riesgo, no solo por número.
  • Agregar pruebas superficiales: las pruebas deben verificar reglas concretas.
  • No revisar ramas parciales: un módulo puede tener buenas sentencias y malas decisiones cubiertas.

20.15 Conclusión

En este tema usamos la cobertura por módulo para decidir dónde enfocar el esfuerzo. El porcentaje total sirve como señal general, pero la priorización real surge al cruzar cobertura, criticidad y comportamiento.

En el próximo tema vamos a combinar resultados de cobertura de varias ejecuciones.