Los datos de prueba son los valores, objetos y condiciones que usamos para ejecutar una prueba. Pueden ser números, textos, listas, fechas, usuarios, productos o cualquier estructura que la unidad necesite.
Elegir buenos datos de prueba es tan importante como escribir buenas aserciones. Datos confusos producen pruebas difíciles de leer; datos excesivos ocultan la intención; datos mal elegidos pueden hacer que la prueba no cubra el riesgo correcto.
En este tema veremos cómo preparar datos simples, claros y mantenibles para pruebas unitarias.
Un buen dato de prueba cumple varias condiciones:
Una buena regla es preparar solo los datos necesarios para el caso. Si una prueba verifica un cálculo de total, no necesita usuario, dirección, sesión y método de pago salvo que esos datos influyan en el cálculo.
def calcular_total(items):
return sum(items)
def test_calcular_total_de_tres_items():
items = [100, 200, 50]
total = calcular_total(items)
assert total == 350
Los datos son mínimos y suficientes: una lista de importes. No hay ruido adicional.
Veamos una versión menos clara:
def test_calcular_total_de_tres_items():
usuario = Usuario("Ana", "ana@example.com")
direccion = Direccion("Calle 1", "Cordoba")
sesion = Sesion(usuario)
items = [100, 200, 50]
total = calcular_total(items)
assert total == 350
Si calcular_total no usa usuario, dirección ni sesión, esos datos distraen. Además, pueden introducir fallas que no tienen relación con el comportamiento probado.
Los nombres de variables deben explicar el rol del dato en la prueba.
def test_aplicar_descuento_del_10_por_ciento():
precio_original = 1000
porcentaje_descuento = 10
precio_final = aplicar_descuento(precio_original, porcentaje_descuento)
assert precio_final == 900
Estos nombres son más claros que x, y o r. En una prueba, la legibilidad vale más que ahorrar algunos caracteres.
Un dato mágico es un valor que aparece sin explicar por qué fue elegido.
def test_descuento():
assert calcular_descuento(13742) == 2061.3
Esta prueba puede ser correcta, pero no queda claro por qué se eligió 13742. Si el objetivo es probar un descuento del 15%, un dato más simple ayuda:
def test_descuento_del_15_por_ciento():
monto = 1000
descuento = calcular_descuento(monto)
assert descuento == 150
El valor 1000 facilita calcular mentalmente el resultado esperado.
Los datos simples no deben ser tan simples que oculten defectos. Por ejemplo, si probamos una multiplicación solo con 1, podríamos no detectar errores.
def test_calcular_total_debil():
assert calcular_total(precio=100, cantidad=1) == 100
Este caso es válido, pero quizá no revela si la cantidad realmente se usa. Un caso con cantidad mayor a 1 puede ser más informativo:
def test_calcular_total_multiplica_precio_por_cantidad():
assert calcular_total(precio=100, cantidad=3) == 300
Cuando la prueba cubre un límite, el dato debe mostrarlo de forma explícita.
def test_edad_18_puede_registrarse():
edad_minima_permitida = 18
resultado = puede_registrarse(edad_minima_permitida)
assert resultado == True
El nombre de la variable explica por qué se eligió 18. Esto puede ser útil cuando el valor tiene significado de negocio.
Las fechas deben elegirse con cuidado. Usar la fecha actual del sistema puede volver la prueba inestable. Es mejor pasar una fecha explícita.
from datetime import date
def cupon_vigente(fecha_actual):
return fecha_actual <= date(2026, 12, 31)
def test_cupon_vigente_en_fecha_limite():
fecha_limite = date(2026, 12, 31)
assert cupon_vigente(fecha_limite) == True
La prueba será repetible porque no depende del día en que se ejecuta.
Al probar texto, conviene usar cadenas que hagan visible la transformación o validación.
def normalizar_nombre(nombre):
return nombre.strip().title()
def test_normalizar_nombre_elimina_espacios_y_capitaliza():
nombre_con_espacios = " ana perez "
resultado = normalizar_nombre(nombre_con_espacios)
assert resultado == "Ana Perez"
El dato incluye espacios y minúsculas porque eso es justamente lo que la función debe corregir.
Las listas deben ser lo bastante pequeñas para leerse, pero suficientes para probar la regla.
def filtrar_pares(numeros):
return [numero for numero in numeros if numero % 2 == 0]
def test_filtrar_pares_devuelve_solo_pares():
numeros = [1, 2, 3, 4]
resultado = filtrar_pares(numeros)
assert resultado == [2, 4]
La lista contiene pares e impares. Si solo tuviera pares, no verificaría bien el filtrado.
Cuando necesitamos objetos, conviene crearlos con la menor información necesaria.
class Cliente:
def __init__(self, tipo):
self.tipo = tipo
def test_cliente_vip_recibe_descuento():
cliente = Cliente(tipo="vip")
descuento = calcular_descuento(cliente, monto=1000)
assert descuento == 150
Si la regla solo depende del tipo de cliente, no hace falta preparar nombre, email, dirección o historial de compras.
Cuando varios tests necesitan objetos similares, podemos crear funciones auxiliares. Pero deben mantenerse simples y explícitas.
def crear_cliente(tipo="comun"):
return Cliente(tipo=tipo)
def test_cliente_vip_recibe_descuento():
cliente = crear_cliente(tipo="vip")
descuento = calcular_descuento(cliente, monto=1000)
assert descuento == 150
El helper reduce repetición sin ocultar el dato importante: el cliente es VIP.
Un helper puede volverse problemático si esconde demasiada información.
def test_cliente_vip_recibe_descuento():
cliente = crear_cliente_para_prueba_completa()
descuento = calcular_descuento(cliente, monto=1000)
assert descuento == 150
No sabemos si el cliente es VIP, común o empleado sin ir a leer el helper. Si ese dato es central para la prueba, debe verse en el propio test o pasarse como argumento explícito.
Una fixture es una preparación reutilizable. Puede ser útil cuando muchas pruebas necesitan el mismo contexto. Sin embargo, una fixture grande puede ocultar demasiados detalles.
Buena fixture:
def carrito_vacio():
return Carrito()
Fixture riesgosa:
def contexto_completo_de_compra():
# Crea usuario, carrito, productos, descuentos, direccion y pago
...
La segunda puede ser útil en pruebas de integración, pero para pruebas unitarias puede ser demasiado amplia.
Reutilizar preparación puede ahorrar repetición, pero no debe sacrificar legibilidad. La regla práctica es: lo importante para entender el caso debe quedar visible.
def test_cliente_empleado_recibe_descuento_20():
cliente = crear_cliente(tipo="empleado")
descuento = calcular_descuento(cliente, monto=1000)
assert descuento == 200
El helper se reutiliza, pero el tipo de cliente sigue explícito.
Compartir datos globales entre pruebas puede generar acoplamiento. Si una prueba modifica esos datos, otra prueba puede fallar dependiendo del orden de ejecución.
Ejemplo riesgoso:
usuarios = []
def test_agregar_usuario():
usuarios.append("Ana")
assert len(usuarios) == 1
Si otra prueba usa la misma lista, el resultado puede cambiar. En pruebas unitarias, conviene que cada prueba prepare sus propios datos o reciba datos nuevos desde una fixture controlada.
| Práctica | Motivo |
|---|---|
| Usar datos mínimos. | Reduce ruido y facilita lectura. |
| Nombrar datos importantes. | Explica por qué fueron elegidos. |
| Evitar valores mágicos. | Hace visible la intención. |
| Usar valores que revelen errores. | Evita pruebas que pasan por casualidad. |
| Crear helpers simples. | Reduce repetición sin ocultar lo importante. |
| Evitar datos globales modificables. | Mantiene independencia entre pruebas. |
Al preparar datos de prueba, revisa:
Los datos de prueba son parte central de la claridad de una prueba unitaria. Datos bien elegidos hacen visible la regla, reducen ruido y facilitan el diagnóstico cuando algo falla.
La meta no es crear datos realistas en exceso, sino datos precisos para el comportamiento que queremos verificar. Simples, expresivos y suficientes.
En el próximo tema veremos cómo organizar archivos y suites de pruebas para mantener una estructura clara a medida que el proyecto crece.