Para medir cobertura necesitamos un proyecto ordenado: código de aplicación por un lado, pruebas por otro y dependencias instaladas en un entorno controlado. Si la estructura es confusa, los reportes de cobertura también serán confusos.
En este tema prepararemos un proyecto Python pequeño, pero realista, que usaremos en los próximos temas para ejecutar pruebas y medir cobertura con herramientas específicas.
Desde la terminal, crea una carpeta para la práctica y entra en ella:
mkdir cobertura-demo
cd cobertura-demo
Usaremos esta carpeta como raíz del proyecto. Todos los comandos del tema se ejecutan desde esta ubicación, salvo que se indique lo contrario.
Un entorno virtual evita mezclar dependencias de distintos proyectos. Créalo con:
python -m venv .venv
Luego actívalo. En Windows PowerShell:
.venv\Scripts\Activate.ps1
En Linux o macOS:
source .venv/bin/activate
Cuando el entorno está activo, la terminal suele mostrar (.venv) al comienzo de la línea.
En este tema todavía no instalaremos la herramienta de cobertura. Primero dejaremos funcionando la suite de pruebas con pytest:
python -m pip install --upgrade pip
python -m pip install pytest
Verifica la instalación:
python -m pytest --version
Crearemos una carpeta para el código de la aplicación y otra para las pruebas:
mkdir src
mkdir tests
mkdir src\tienda
En Linux o macOS, el último comando puede escribirse así:
mkdir -p src/tienda
La estructura inicial será:
cobertura-demo/
|-- .venv/
|-- src/
| `-- tienda/
`-- tests/
Dentro de src/tienda, crea un archivo vacío llamado __init__.py. Este archivo indica que tienda debe tratarse como un paquete Python.
En Windows PowerShell puedes crearlo con:
New-Item -ItemType File src\tienda\__init__.py
En Linux o macOS:
touch src/tienda/__init__.py
Ahora crea el archivo src/tienda/precios.py con este contenido:
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
Este módulo tiene validaciones, cálculos y un ciclo. Es pequeño, pero suficiente para observar líneas cubiertas y no cubiertas cuando midamos cobertura.
Crea el archivo tests/test_precios.py:
import pytest
from tienda.precios import aplicar_descuento, calcular_total
def test_aplicar_descuento():
assert aplicar_descuento(1000, 10) == 900
def test_aplicar_descuento_rechaza_precio_invalido():
with pytest.raises(ValueError):
aplicar_descuento(0, 10)
def test_calcular_total_con_items():
items = [
{"precio": 100, "cantidad": 2},
{"precio": 50, "cantidad": 3},
]
assert calcular_total(items) == 350
Estas pruebas no cubren todos los caminos del módulo. Esa decisión es intencional: en los próximos temas veremos cómo el reporte de cobertura nos ayuda a encontrar lo que falta.
Como el código está dentro de la carpeta src, Python debe poder encontrar el paquete tienda. Una forma simple para esta práctica es definir PYTHONPATH al ejecutar las pruebas.
En Windows PowerShell:
$env:PYTHONPATH="src"
python -m pytest
En Linux o macOS:
PYTHONPATH=src python -m pytest
Si todo está correcto, las pruebas deben pasar.
La salida exacta puede variar según la versión, pero debería mostrar que se ejecutaron tres pruebas correctamente:
3 passed
Este resultado solo confirma que las pruebas pasan. Todavía no sabemos qué partes del módulo quedaron sin ejecutar. Esa información aparecerá cuando midamos cobertura.
Para registrar las dependencias mínimas de este proyecto, crea un archivo requirements.txt con este contenido:
pytest
Más adelante agregaremos las herramientas de cobertura. Por ahora, este archivo expresa que el proyecto necesita pytest para ejecutar sus pruebas.
Al terminar el tema, la estructura debería quedar así:
cobertura-demo/
|-- .venv/
|-- requirements.txt
|-- src/
| `-- tienda/
| |-- __init__.py
| `-- precios.py
`-- tests/
`-- test_precios.py
Esta organización separa claramente el código productivo de las pruebas. Esa separación facilita leer los reportes de cobertura y evita medir archivos equivocados.
Antes de usar herramientas automáticas, conviene mirar el código y razonar. En nuestro ejemplo todavía faltan pruebas para varios casos:
calcular_total.La cobertura nos ayudará a confirmar estas ausencias, pero el análisis humano sigue siendo necesario para decidir qué pruebas tienen sentido.
PYTHONPATH=src o $env:PYTHONPATH="src" antes de ejecutar pytest.tests y el archivo comience con test_.python -m pytest.cobertura-demo.En este tema dejamos listo un proyecto Python preparado para medir cobertura: creamos carpetas, un paquete dentro de src, un módulo con lógica de negocio y una suite inicial de pruebas con pytest.
También vimos que las pruebas pasan, pero aún no sabemos qué líneas quedaron sin ejecutar. En el próximo tema instalaremos coverage.py y haremos la primera medición real desde la terminal.