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.
pragma: no cover solo en casos justificados y verificar cómo cambia el reporte.
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.
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.
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.
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.
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.
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.
Algunos casos donde puede estar justificado excluir son:
if __name__ == "__main__".No uses pragma: no cover para evitar escribir pruebas importantes.
pytest.raises.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.
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.
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.
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:
pragma: no cover en el proyecto.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.