Muchas aplicaciones trabajan con colecciones de datos: listas de ventas, diccionarios de usuarios, fechas de pedidos o reportes por período. Estos casos suelen tener errores en filtros, orden, acumulaciones y límites de fecha.
En este tema probaremos funciones que procesan listas, diccionarios y fechas usando pytest.
Crea un proyecto nuevo:
mkdir pytest-colecciones-fechas-demo
cd pytest-colecciones-fechas-demo
Si pytest no está instalado en el entorno activo:
python -m pip install pytest
Crea un archivo llamado reportes.py:
from datetime import date
def calcular_total_ventas(ventas):
return sum(venta["importe"] for venta in ventas)
def filtrar_ventas_por_cliente(ventas, cliente):
return [venta for venta in ventas if venta["cliente"] == cliente]
def obtener_clientes_unicos(ventas):
return sorted({venta["cliente"] for venta in ventas})
def agrupar_total_por_cliente(ventas):
totales = {}
for venta in ventas:
cliente = venta["cliente"]
totales[cliente] = totales.get(cliente, 0) + venta["importe"]
return totales
def filtrar_ventas_por_fecha(ventas, desde, hasta):
return [
venta
for venta in ventas
if desde <= venta["fecha"] <= hasta
]
def venta_mas_reciente(ventas):
if not ventas:
return None
return max(ventas, key=lambda venta: venta["fecha"])
def dias_entre(inicio, fin):
return (fin - inicio).days
Usamos datetime.date para representar fechas sin hora.
Crea test_reportes.py y prepara ventas reutilizables:
from datetime import date
import pytest
@pytest.fixture
def ventas():
return [
{"cliente": "Ana", "importe": 1000, "fecha": date(2026, 5, 1)},
{"cliente": "Luis", "importe": 500, "fecha": date(2026, 5, 2)},
{"cliente": "Ana", "importe": 1500, "fecha": date(2026, 5, 3)},
]
La fixture permite reutilizar la misma lista en varias pruebas sin repetirla.
Agrega esta prueba:
from reportes import calcular_total_ventas
def test_calcular_total_ventas(ventas):
resultado = calcular_total_ventas(ventas)
assert resultado == 3000
La prueba verifica que se sumen todos los importes de la lista.
Una lista vacía debe devolver total cero:
def test_calcular_total_ventas_con_lista_vacia():
assert calcular_total_ventas([]) == 0
Este caso evita errores cuando no hay ventas para procesar.
Para filtrar por cliente:
from reportes import filtrar_ventas_por_cliente
def test_filtrar_ventas_por_cliente(ventas):
resultado = filtrar_ventas_por_cliente(ventas, "Ana")
assert resultado == [
{"cliente": "Ana", "importe": 1000, "fecha": date(2026, 5, 1)},
{"cliente": "Ana", "importe": 1500, "fecha": date(2026, 5, 3)},
]
Cuando comparamos listas de diccionarios, importan los valores y el orden.
Si no hay coincidencias, esperamos una lista vacía:
def test_filtrar_ventas_por_cliente_sin_resultados(ventas):
resultado = filtrar_ventas_por_cliente(ventas, "Marta")
assert resultado == []
La función devuelve nombres únicos ordenados:
from reportes import obtener_clientes_unicos
def test_obtener_clientes_unicos(ventas):
resultado = obtener_clientes_unicos(ventas)
assert resultado == ["Ana", "Luis"]
Usar orden explícito hace que el resultado sea fácil de comparar.
Para totales por cliente:
from reportes import agrupar_total_por_cliente
def test_agrupar_total_por_cliente(ventas):
resultado = agrupar_total_por_cliente(ventas)
assert resultado == {
"Ana": 2500,
"Luis": 500,
}
Los diccionarios se comparan por claves y valores.
Importa filtrar_ventas_por_fecha y prueba un rango:
from reportes import filtrar_ventas_por_fecha
def test_filtrar_ventas_por_fecha(ventas):
resultado = filtrar_ventas_por_fecha(
ventas,
date(2026, 5, 2),
date(2026, 5, 3),
)
assert resultado == [
{"cliente": "Luis", "importe": 500, "fecha": date(2026, 5, 2)},
{"cliente": "Ana", "importe": 1500, "fecha": date(2026, 5, 3)},
]
El rango incluye fecha inicial y fecha final.
Conviene probar que los límites son inclusivos:
def test_filtrar_ventas_por_fecha_incluye_limites(ventas):
resultado = filtrar_ventas_por_fecha(
ventas,
date(2026, 5, 1),
date(2026, 5, 1),
)
assert resultado == [
{"cliente": "Ana", "importe": 1000, "fecha": date(2026, 5, 1)},
]
La función debe devolver el diccionario con fecha mayor:
from reportes import venta_mas_reciente
def test_venta_mas_reciente(ventas):
resultado = venta_mas_reciente(ventas)
assert resultado == {"cliente": "Ana", "importe": 1500, "fecha": date(2026, 5, 3)}
También debemos cubrir lista vacía:
def test_venta_mas_reciente_con_lista_vacia():
assert venta_mas_reciente([]) is None
Para calcular días entre dos fechas:
from reportes import dias_entre
def test_dias_entre_fechas():
resultado = dias_entre(date(2026, 5, 1), date(2026, 5, 10))
assert resultado == 9
La diferencia entre fechas devuelve la cantidad de días completos entre ambas.
En el archivo completo usaremos una prueba parametrizada para reemplazar la prueba individual anterior y cubrir varios rangos con la misma función de prueba:
@pytest.mark.parametrize("inicio, fin, esperado", [
(date(2026, 5, 1), date(2026, 5, 1), 0),
(date(2026, 5, 1), date(2026, 5, 2), 1),
(date(2026, 5, 1), date(2026, 5, 10), 9),
])
def test_dias_entre_parametrizado(inicio, fin, esperado):
assert dias_entre(inicio, fin) == esperado
El archivo test_reportes.py puede quedar así:
from datetime import date
import pytest
from reportes import (
agrupar_total_por_cliente,
calcular_total_ventas,
dias_entre,
filtrar_ventas_por_cliente,
filtrar_ventas_por_fecha,
obtener_clientes_unicos,
venta_mas_reciente,
)
@pytest.fixture
def ventas():
return [
{"cliente": "Ana", "importe": 1000, "fecha": date(2026, 5, 1)},
{"cliente": "Luis", "importe": 500, "fecha": date(2026, 5, 2)},
{"cliente": "Ana", "importe": 1500, "fecha": date(2026, 5, 3)},
]
def test_calcular_total_ventas(ventas):
assert calcular_total_ventas(ventas) == 3000
def test_calcular_total_ventas_con_lista_vacia():
assert calcular_total_ventas([]) == 0
def test_filtrar_ventas_por_cliente(ventas):
resultado = filtrar_ventas_por_cliente(ventas, "Ana")
assert resultado == [
{"cliente": "Ana", "importe": 1000, "fecha": date(2026, 5, 1)},
{"cliente": "Ana", "importe": 1500, "fecha": date(2026, 5, 3)},
]
def test_filtrar_ventas_por_cliente_sin_resultados(ventas):
assert filtrar_ventas_por_cliente(ventas, "Marta") == []
def test_obtener_clientes_unicos(ventas):
assert obtener_clientes_unicos(ventas) == ["Ana", "Luis"]
def test_agrupar_total_por_cliente(ventas):
assert agrupar_total_por_cliente(ventas) == {
"Ana": 2500,
"Luis": 500,
}
def test_filtrar_ventas_por_fecha(ventas):
resultado = filtrar_ventas_por_fecha(
ventas,
date(2026, 5, 2),
date(2026, 5, 3),
)
assert resultado == [
{"cliente": "Luis", "importe": 500, "fecha": date(2026, 5, 2)},
{"cliente": "Ana", "importe": 1500, "fecha": date(2026, 5, 3)},
]
def test_filtrar_ventas_por_fecha_incluye_limites(ventas):
resultado = filtrar_ventas_por_fecha(
ventas,
date(2026, 5, 1),
date(2026, 5, 1),
)
assert resultado == [
{"cliente": "Ana", "importe": 1000, "fecha": date(2026, 5, 1)},
]
def test_venta_mas_reciente(ventas):
assert venta_mas_reciente(ventas) == {
"cliente": "Ana",
"importe": 1500,
"fecha": date(2026, 5, 3),
}
def test_venta_mas_reciente_con_lista_vacia():
assert venta_mas_reciente([]) is None
@pytest.mark.parametrize("inicio, fin, esperado", [
(date(2026, 5, 1), date(2026, 5, 1), 0),
(date(2026, 5, 1), date(2026, 5, 2), 1),
(date(2026, 5, 1), date(2026, 5, 10), 9),
])
def test_dias_entre_parametrizado(inicio, fin, esperado):
assert dias_entre(inicio, fin) == esperado
Ejecuta:
python -m pytest
La salida esperada será similar a:
collected 13 items
test_reportes.py ............. [100%]
13 passed in 0.04s
Cuando una función devuelve una lista, decide si el orden forma parte del comportamiento. Si importa, compara la lista completa:
assert resultado == ["Ana", "Luis"]
Si el orden no importa, puedes comparar conjuntos:
assert set(resultado) == {"Ana", "Luis"}
Si toda la estructura importa, compara el diccionario completo. Si solo importa una regla, verifica una clave:
assert resultado["Ana"] == 2500
Esto evita pruebas demasiado rígidas cuando el diccionario puede crecer con más datos.
date para trabajar con fechas reales.mkdir pytest-colecciones-fechas-demo
cd pytest-colecciones-fechas-demo
python -m pip install pytest
python -m pytest
python -m pytest -v
python -m pytest test_reportes.py::test_filtrar_ventas_por_fecha -v
datetime.date.En este tema probamos funciones que trabajan con listas, diccionarios y fechas. Vimos filtros, totales, agrupaciones, orden, listas vacías y límites de fecha.
En el próximo tema trabajaremos con lectura y escritura de archivos temporales, una necesidad frecuente al probar código que interactúa con el sistema de archivos.