En el tema anterior usamos parametrización para ejecutar una prueba con varios escenarios. Ahora veremos distintas formas de organizar los datos que alimentan esas pruebas.
Trabajaremos con listas, diccionarios, archivos JSON y archivos CSV. La meta es elegir el formato adecuado según la cantidad de datos, la claridad y el mantenimiento.
Los datos de prueba pueden vivir en distintos lugares:
Para pocos casos, una lista dentro de la prueba parametrizada puede ser suficiente:
import pytest
from app.cupones import validar_cupon
@pytest.mark.parametrize("cupon, esperado", [
("DESC10", True),
("desc10", True),
("DESC20", False),
])
def test_validar_cupon_devuelve_resultado_esperado(cupon, esperado):
assert validar_cupon(cupon) is esperado
Esta opción es simple y muy legible cuando la cantidad de casos es baja.
Cuando cada caso tiene varios campos, una lista de diccionarios puede ser más clara:
CASOS_CUPONES = [
{"cupon": "DESC10", "esperado": True},
{"cupon": "desc10", "esperado": True},
{"cupon": "DESC20", "esperado": False},
]
Uso con parametrización:
@pytest.mark.parametrize("caso", CASOS_CUPONES)
def test_validar_cupon_con_diccionarios_devuelve_resultado_esperado(caso):
assert validar_cupon(caso["cupon"]) is caso["esperado"]
Si todavía no existe, crea la carpeta para datos de prueba:
mkdir tests\data
En Linux o macOS:
mkdir -p tests/data
Allí guardaremos archivos JSON y CSV usados por las pruebas.
Crea el archivo tests/data/cupones.json:
[
{
"cupon": "DESC10",
"esperado": true
},
{
"cupon": "desc10",
"esperado": true
},
{
"cupon": "DESC20",
"esperado": false
},
{
"cupon": "",
"esperado": false
}
]
JSON es práctico cuando los datos tienen estructura y nombres de campos.
Crea tests/test_cupones_json.py:
import json
from pathlib import Path
import pytest
from app.cupones import validar_cupon
DATA_DIR = Path(__file__).parent / "data"
def cargar_cupones_json():
ruta = DATA_DIR / "cupones.json"
return json.loads(ruta.read_text(encoding="utf-8"))
@pytest.mark.parametrize("caso", cargar_cupones_json())
def test_validar_cupon_con_datos_json_devuelve_resultado_esperado(caso):
assert validar_cupon(caso["cupon"]) is caso["esperado"]
Usamos Path(__file__).parent para ubicar el archivo de datos desde la carpeta del archivo de prueba.
Ejecuta:
python -m pytest tests/test_cupones_json.py
pytest cargará los datos del archivo JSON y generará un caso por cada elemento de la lista.
Podemos agregar un campo id al JSON para mejorar la salida:
[
{
"id": "codigo_correcto",
"cupon": "DESC10",
"esperado": true
},
{
"id": "codigo_incorrecto",
"cupon": "DESC20",
"esperado": false
}
]
Y usarlo en la parametrización:
casos = cargar_cupones_json()
@pytest.mark.parametrize("caso", casos, ids=[caso["id"] for caso in casos])
def test_validar_cupon_con_datos_json_devuelve_resultado_esperado(caso):
assert validar_cupon(caso["cupon"]) is caso["esperado"]
Crea el archivo tests/data/descuentos.csv:
precio,descuento,esperado
100,0,100
100,10,90
100,100,0
250,20,200
CSV es cómodo cuando los datos son tabulares y todos los casos tienen las mismas columnas.
Crea tests/test_precios_csv.py:
import csv
from pathlib import Path
import pytest
from app.precios import calcular_precio_final
DATA_DIR = Path(__file__).parent / "data"
def cargar_descuentos_csv():
ruta = DATA_DIR / "descuentos.csv"
with ruta.open(encoding="utf-8", newline="") as archivo:
return list(csv.DictReader(archivo))
@pytest.mark.parametrize("caso", cargar_descuentos_csv())
def test_calcular_precio_final_con_datos_csv_devuelve_resultado_esperado(caso):
precio = float(caso["precio"])
descuento = float(caso["descuento"])
esperado = float(caso["esperado"])
assert calcular_precio_final(precio, descuento) == esperado
csv.DictReader devuelve cada fila como diccionario usando los encabezados del archivo.
Ejecuta:
python -m pytest tests/test_precios_csv.py
Si todo está correcto, se ejecutará un caso por cada fila del CSV.
Si varias pruebas leen archivos, conviene extraer funciones a tests/helpers/data_helper.py:
import csv
import json
from pathlib import Path
DATA_DIR = Path(__file__).parents[1] / "data"
def cargar_json(nombre_archivo):
ruta = DATA_DIR / nombre_archivo
return json.loads(ruta.read_text(encoding="utf-8"))
def cargar_csv(nombre_archivo):
ruta = DATA_DIR / nombre_archivo
with ruta.open(encoding="utf-8", newline="") as archivo:
return list(csv.DictReader(archivo))
Así las pruebas no repiten lógica de lectura.
La prueba puede quedar más corta:
import pytest
from app.cupones import validar_cupon
from tests.helpers.data_helper import cargar_json
casos = cargar_json("cupones.json")
@pytest.mark.parametrize("caso", casos, ids=[caso.get("id", caso["cupon"]) for caso in casos])
def test_validar_cupon_con_helper_json_devuelve_resultado_esperado(caso):
assert validar_cupon(caso["cupon"]) is caso["esperado"]
La prueba se concentra en la verificación y el helper se encarga de la lectura.
Cuando los datos vienen de archivos, conviene validar que tengan los campos esperados:
def validar_caso_cupon(caso):
campos_requeridos = {"cupon", "esperado"}
faltantes = campos_requeridos - set(caso)
if faltantes:
raise ValueError(f"Faltan campos en el caso: {faltantes}")
Esto ayuda a detectar errores en los archivos de datos antes de que la prueba falle con un mensaje confuso.
Usa listas o diccionarios dentro del archivo de prueba cuando:
Usa JSON cuando:
Usa CSV cuando:
Los archivos de datos deben ser útiles, no depósitos desordenados. Evita:
data.json.Path.int o float.Crea un archivo tests/data/usuarios.json con casos para validar si un usuario está activo. Cada caso debe tener:
idusuarioesperadoLuego crea una prueba parametrizada que lea ese JSON y verifique usuario_esta_activo.
Archivo tests/data/usuarios.json:
[
{
"id": "usuario_activo",
"usuario": {
"nombre": "Ana",
"email": "ana@example.com",
"activo": true
},
"esperado": true
},
{
"id": "usuario_inactivo",
"usuario": {
"nombre": "Luis",
"email": "luis@example.com",
"activo": false
},
"esperado": false
}
]
Archivo tests/test_usuarios_json.py:
import pytest
from app.usuarios import usuario_esta_activo
from tests.helpers.data_helper import cargar_json
casos = cargar_json("usuarios.json")
@pytest.mark.parametrize("caso", casos, ids=[caso["id"] for caso in casos])
def test_usuario_esta_activo_con_datos_json_devuelve_resultado_esperado(caso):
assert usuario_esta_activo(caso["usuario"]) is caso["esperado"]
Ejecuta:
python -m pytest tests/test_usuarios_json.py
Antes de continuar con el próximo tema, verifica lo siguiente:
python -m pytest.En este tema usamos datos de prueba desde listas, diccionarios, JSON y CSV. Cada formato tiene ventajas según el tamaño, la estructura y la reutilización de los datos.
En el próximo tema veremos cómo generar datos de prueba de forma controlada con Python, evitando depender siempre de archivos estáticos.