En el tema anterior dejamos listo un proyecto Python con código en src y pruebas en tests. Ahora instalaremos coverage.py, ejecutaremos las pruebas bajo medición y generaremos el primer reporte de cobertura desde la terminal.
El objetivo no es alcanzar 100% de cobertura todavía. Primero necesitamos entender el flujo básico: instalar la herramienta, ejecutar las pruebas, generar el reporte y leer los resultados principales.
Trabajaremos sobre el proyecto cobertura-demo creado en el tema anterior. Antes de continuar, entra en la carpeta y activa el entorno virtual.
En Windows PowerShell:
cd cobertura-demo
.venv\Scripts\Activate.ps1
En Linux o macOS:
cd cobertura-demo
source .venv/bin/activate
La estructura esperada es:
cobertura-demo/
|-- .venv/
|-- requirements.txt
|-- src/
| `-- tienda/
| |-- __init__.py
| `-- precios.py
`-- tests/
`-- test_precios.py
Antes de medir cobertura, confirma que la suite de pruebas funciona. En Windows PowerShell:
$env:PYTHONPATH="src"
python -m pytest
En Linux o macOS:
PYTHONPATH=src python -m pytest
Debes obtener una salida similar a:
3 passed
Con el entorno virtual activo, instala coverage.py:
python -m pip install coverage
Luego verifica la instalación:
python -m coverage --version
Una salida válida mostrará la versión instalada de coverage.py. El número exacto puede variar.
Agrega coverage al archivo requirements.txt para que el proyecto declare sus dependencias de pruebas:
pytest
coverage
Otra opción es regenerar el archivo con:
python -m pip freeze > requirements.txt
Para ejercicios didácticos suele ser más claro escribir solo las dependencias principales. En proyectos reales puede convenir fijar versiones exactas.
Para medir cobertura, ejecutamos las pruebas mediante coverage run. En Windows PowerShell:
$env:PYTHONPATH="src"
python -m coverage run -m pytest
En Linux o macOS:
PYTHONPATH=src python -m coverage run -m pytest
La parte -m pytest indica que coverage debe ejecutar el módulo pytest. Es decir: las pruebas corren igual que antes, pero ahora se registra qué código se ejecutó.
Después de ejecutar el comando anterior, aparecerá un archivo llamado .coverage en la raíz del proyecto.
Ese archivo contiene los datos crudos de la medición. No se lee manualmente; se usa como entrada para generar reportes.
cobertura-demo/
|-- .coverage
|-- requirements.txt
|-- src/
`-- tests/
Para ver el reporte en la terminal, ejecuta:
python -m coverage report
Luego de ejecutar el comando, se obtuvo esta salida:
Name Stmts Miss Cover
--------------------------------------------
src\tienda\__init__.py 0 0 100%
src\tienda\precios.py 14 2 86%
tests\test_precios.py 10 0 100%
--------------------------------------------
TOTAL 24 2 92%
En este caso, el reporte muestra una cobertura total del 92% y dos sentencias sin ejecutar.
En el ejemplo, src/tienda/precios.py tiene líneas sin cubrir. Eso coincide con lo que ya habíamos razonado: faltan pruebas para algunos caminos.
El reporte básico muestra el porcentaje, pero no dice cuáles son las líneas faltantes. Para verlas, usa la opción -m:
python -m coverage report -m
La salida agrega una columna Missing:
Name Stmts Miss Cover Missing
------------------------------------------------------
src\tienda\__init__.py 0 0 100%
src\tienda\precios.py 14 2 86% 6, 14
tests\test_precios.py 10 0 100%
------------------------------------------------------
TOTAL 24 2 92%
Los números de línea indican dónde quedaron partes del código sin ejecutar durante la suite. En este caso, las líneas faltantes son la 6 y la 14 de src\tienda\precios.py.
Volvamos al módulo src/tienda/precios.py:
def aplicar_descuento(precio, porcentaje):
if precio <= 0:
raise ValueError("El precio debe ser mayor que cero")
if porcentaje < 0 or porcentaje > 100:
raise ValueError("El porcentaje debe estar entre 0 y 100")
descuento = precio * porcentaje / 100
return precio - descuento
def calcular_total(items):
if not items:
return 0
total = 0
for item in items:
total += item["precio"] * item["cantidad"]
return total
Las líneas faltantes corresponden a caminos no probados: porcentaje inválido y lista vacía. El reporte confirma dónde conviene agregar nuevas pruebas.
Agrega estas pruebas al archivo tests/test_precios.py:
def test_aplicar_descuento_rechaza_porcentaje_menor_que_cero():
with pytest.raises(ValueError):
aplicar_descuento(1000, -1)
def test_aplicar_descuento_rechaza_porcentaje_mayor_que_cien():
with pytest.raises(ValueError):
aplicar_descuento(1000, 101)
def test_calcular_total_sin_items():
assert calcular_total([]) == 0
Estas pruebas no se escriben para perseguir un número. Se escriben porque representan comportamientos reales que no estaban verificados.
Ejecuta otra vez las pruebas con cobertura. En Windows PowerShell:
$env:PYTHONPATH="src"
python -m coverage run -m pytest
python -m coverage report -m
En Linux o macOS:
PYTHONPATH=src python -m coverage run -m pytest
python -m coverage report -m
Ahora el porcentaje debería subir y la lista de líneas faltantes debería reducirse o desaparecer para precios.py.
Si quieres borrar los datos acumulados de cobertura, ejecuta:
python -m coverage erase
Esto elimina el archivo .coverage. Luego puedes volver a medir desde cero ejecutando python -m coverage run -m pytest.
python -m pip install coverage.PYTHONPATH apunte a src.python -m coverage run -m pytest y luego python -m coverage report.En este tema instalamos coverage.py, ejecutamos las pruebas bajo medición con coverage run -m pytest y generamos el primer reporte con coverage report.
También usamos coverage report -m para localizar líneas faltantes y agregamos pruebas a partir de esa información. En el próximo tema profundizaremos en la ejecución con coverage run y en la generación del reporte básico.