17. Excluir líneas justificadas con pragma: no cover

17.1 Objetivo del tema

Hay líneas que no conviene medir con cobertura porque no representan lógica que valga la pena probar con tests unitarios. Para esos casos, coverage.py permite excluir líneas con pragma: no cover.

La exclusión debe usarse con criterio. No es una herramienta para ocultar código difícil de probar, sino para dejar fuera líneas que tienen una justificación técnica clara.

Objetivo práctico: aplicar pragma: no cover solo en casos justificados y verificar cómo cambia el reporte.

17.2 Qué significa excluir una línea

Excluir una línea significa que coverage no la tendrá en cuenta al calcular el porcentaje. Si esa línea no se ejecuta, no aparecerá como faltante.

Por ejemplo:

if __name__ == "__main__":  # pragma: no cover
    main()

Esta línea suele excluirse porque el bloque de entrada manual del programa no se prueba igual que la lógica principal.

17.3 Crear un módulo de ejemplo

Crea el archivo src/tienda/consola.py:

from tienda.tarifas import calcular_tarifa


def mostrar_tarifa(zona, peso):
    tarifa = calcular_tarifa(zona, peso)
    return f"La tarifa es ${tarifa}"


def main():
    zona = input("Zona: ")
    peso = float(input("Peso: "))
    print(mostrar_tarifa(zona, peso))


if __name__ == "__main__":
    main()

La función mostrar_tarifa es fácil de probar. El bloque if __name__ == "__main__" es solo el punto de entrada cuando se ejecuta el archivo manualmente.

17.4 Probar la lógica principal

Crea una prueba para el comportamiento que sí importa:

from tienda.consola import mostrar_tarifa


def test_mostrar_tarifa():
    assert mostrar_tarifa("local", 1) == "La tarifa es $1000"

Esta prueba verifica la función que formatea el resultado. No necesita ejecutar la entrada interactiva por consola.

17.5 Medir cobertura

Ejecuta el reporte:

En Windows PowerShell:

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

En Linux o macOS:

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

Es probable que el reporte marque como faltantes las líneas del bloque de ejecución manual.

17.6 Aplicar pragma: no cover

Podemos excluir el bloque de entrada manual:

if __name__ == "__main__":  # pragma: no cover
    main()

Cuando se aplica sobre una línea de control como if, coverage excluye también el bloque asociado.

Vuelve a ejecutar el reporte y revisa que esas líneas ya no aparezcan como faltantes.

17.7 Ver exclusiones en el reporte

Si tienes configurado el reporte para mostrar líneas excluidas, o si usas el reporte HTML, podrás distinguir código faltante de código excluido.

Con una configuración como esta:

[tool.coverage.report]
show_missing = true
precision = 2

El reporte deja de penalizar las líneas marcadas con pragma: no cover.

17.8 Casos razonables para excluir

Algunos casos donde puede estar justificado excluir son:

  • Puntos de entrada manual: bloques if __name__ == "__main__".
  • Código defensivo imposible de alcanzar en uso normal: siempre que haya una razón clara.
  • Adaptadores mínimos a herramientas externas: cuando la lógica real está probada en otra función.
  • Ramas específicas de plataforma: si no se ejecutan en el entorno de pruebas actual.

17.9 Casos donde no conviene excluir

No uses pragma: no cover para evitar escribir pruebas importantes.

  • Validaciones de negocio: deben probarse.
  • Manejo de errores esperado: normalmente se prueba con pytest.raises.
  • Ramas de permisos o seguridad: suelen ser críticas.
  • Código complejo: si es difícil de probar, quizá necesita rediseño.

17.10 Excluir una línea concreta

También puedes excluir una sola línea:

def entorno_actual():
    return "windows" if os.name == "nt" else "posix"  # pragma: no cover

Esto debe usarse con cuidado. Si la línea contiene lógica importante, es preferible probarla o separarla en una función más fácil de verificar.

17.11 Excluir ramas por configuración

Coverage también permite configurar patrones de exclusión en pyproject.toml:

[tool.coverage.report]
exclude_lines = [
    "pragma: no cover",
    "if __name__ == .__main__.:",
]

En general, conviene empezar con pragma: no cover explícito. Es más visible y obliga a justificar caso por caso.

17.12 Documentar la razón

Cuando la exclusión no sea obvia, agrega un comentario breve:

if sistema_no_soportado():  # pragma: no cover
    raise RuntimeError("Sistema no soportado")  # solo se ejecuta fuera de CI

El comentario debe explicar por qué no se mide esa línea, no repetir lo que el código ya dice.

17.13 Revisar exclusiones periódicamente

Las exclusiones pueden quedar obsoletas. Un caso que antes era difícil de probar puede volverse fácil después de refactorizar el código.

Conviene revisar periódicamente:

  • Si la línea excluida todavía existe por una razón válida.
  • Si la lógica puede moverse a una función probada.
  • Si la exclusión está ocultando un comportamiento importante.
  • Si hay demasiados pragma: no cover en el proyecto.

17.14 Errores frecuentes

  • Usar pragma para subir el porcentaje: una exclusión injustificada reduce la utilidad del reporte.
  • Excluir validaciones importantes: las reglas de negocio deben probarse.
  • No dejar contexto: si la razón no es obvia, agrega un comentario corto.
  • Olvidar el reporte HTML: úsalo para revisar visualmente qué quedó excluido.

17.15 Conclusión

En este tema usamos pragma: no cover para excluir líneas justificadas del cálculo de cobertura. La regla principal es simple: excluir solo cuando hay una razón técnica clara.

En el próximo tema vamos a analizar archivos que no conviene medir, como pruebas, scripts auxiliares y código generado.