15. Datos de prueba desde listas, diccionarios, CSV y JSON

15.1 Objetivo del tema

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.

Objetivo práctico: cargar datos de prueba desde estructuras Python y archivos externos para usarlos en pruebas parametrizadas.

15.2 Tipos de datos de prueba

Los datos de prueba pueden vivir en distintos lugares:

  • Dentro de la prueba: útil para pocos casos simples.
  • En listas o diccionarios: útil para escenarios pequeños y legibles.
  • En JSON: útil para datos jerárquicos o parecidos a objetos.
  • En CSV: útil para tablas de casos simples.
  • En helpers: útil cuando la preparación tiene lógica reutilizable.

15.3 Datos embebidos en la prueba

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.

15.4 Datos en una lista de diccionarios

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"]

15.5 Crear la carpeta tests/data

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.

15.6 Datos desde JSON

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.

15.7 Leer JSON desde una prueba

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.

15.8 Ejecutar la prueba con JSON

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.

15.9 Agregar IDs desde JSON

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"]

15.10 Datos desde CSV

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.

15.11 Leer CSV desde una prueba

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.

15.12 Ejecutar la prueba con CSV

Ejecuta:

python -m pytest tests/test_precios_csv.py

Si todo está correcto, se ejecutará un caso por cada fila del CSV.

15.13 Crear helpers para lectura de datos

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.

15.14 Usar el helper desde una prueba

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.

15.15 Validar datos antes de usarlos

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.

15.16 Cuándo usar listas o diccionarios

Usa listas o diccionarios dentro del archivo de prueba cuando:

  • Hay pocos casos.
  • Los datos se entienden de un vistazo.
  • No necesitas compartirlos con otras pruebas.
  • Quieres que la prueba sea autocontenida.

15.17 Cuándo usar JSON

Usa JSON cuando:

  • Los datos tienen estructura con campos anidados.
  • Quieres nombres de campos claros.
  • Los datos se parecen a objetos de negocio.
  • Necesitas compartir los mismos casos entre varias pruebas.

15.18 Cuándo usar CSV

Usa CSV cuando:

  • Los datos son una tabla simple.
  • Todos los casos tienen las mismas columnas.
  • Quieres editar muchos casos de forma rápida.
  • Los valores son principalmente textos o números simples.

15.19 Evitar datos de prueba inmanejables

Los archivos de datos deben ser útiles, no depósitos desordenados. Evita:

  • Archivos enormes sin explicación.
  • Casos duplicados.
  • Datos que no se usan.
  • Nombres de archivo genéricos como data.json.
  • Valores esperados difíciles de verificar.

15.20 Problemas frecuentes

  • No se encuentra el archivo: revisa la ruta construida con Path.
  • Los números del CSV llegan como texto: convierte con int o float.
  • El JSON no carga: verifica comas, comillas y valores booleanos en minúscula.
  • La salida parametrizada es confusa: agrega IDs a los casos.
  • Hay demasiados datos: separa archivos por intención o funcionalidad.

15.21 Ejercicio práctico

Crea un archivo tests/data/usuarios.json con casos para validar si un usuario está activo. Cada caso debe tener:

  • id
  • usuario
  • esperado

Luego crea una prueba parametrizada que lea ese JSON y verifique usuario_esta_activo.

15.22 Solución propuesta

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

15.23 Lista de verificación

Antes de continuar con el próximo tema, verifica lo siguiente:

  • Sabes cuándo usar datos embebidos en la prueba.
  • Sabes cargar datos desde JSON.
  • Sabes cargar datos desde CSV.
  • Conviertes tipos cuando los datos vienen como texto.
  • Usas IDs para mejorar la salida parametrizada.
  • Los archivos de datos tienen nombres claros.
  • La suite se ejecuta correctamente con python -m pytest.

15.24 Conclusión

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.