En el tema anterior instalamos pytest y ejecutamos una primera prueba. Ahora vamos a escribir pruebas simples con más variedad: números, cadenas, booleanos, listas, diccionarios y excepciones.
El objetivo es acostumbrarnos al estilo de pytest: funciones con nombre test_..., aserciones con assert y ejecución desde la terminal con python -m pytest.
assert.
Crea un proyecto nuevo:
mkdir pytest-pruebas-simples-demo
cd pytest-pruebas-simples-demo
Si todavía no tienes pytest instalado en el entorno activo, instálalo con:
python -m pip install pytest
Crea un archivo llamado servicios.py:
def calcular_total(precio, cantidad):
return precio * cantidad
def normalizar_email(email):
return email.strip().lower()
def es_usuario_activo(usuario):
return usuario.get("activo", False)
def obtener_nombres(productos):
return [producto["nombre"] for producto in productos]
def crear_resumen_producto(nombre, precio):
return {
"nombre": nombre.strip().title(),
"precio": precio,
"disponible": precio > 0,
}
def aplicar_cupon(total, cupon):
if cupon == "DESC10":
return total * 0.9
if cupon == "DESC20":
return total * 0.8
raise ValueError("Cupón inválido")
Estas funciones son pequeñas y nos permiten practicar distintos tipos de aserciones.
Crea un archivo llamado test_servicios.py:
from servicios import calcular_total
def test_calcular_total():
resultado = calcular_total(1000, 3)
assert resultado == 3000
La función de prueba comienza con test_. Dentro usamos assert para comparar el resultado obtenido con el resultado esperado.
Desde la raíz del proyecto, ejecuta:
python -m pytest
La salida esperada será similar a:
collected 1 item
test_servicios.py . [100%]
1 passed in 0.02s
Importa normalizar_email y agrega una prueba:
from servicios import calcular_total, normalizar_email
def test_normalizar_email():
resultado = normalizar_email(" ANA@EXAMPLE.COM ")
assert resultado == "ana@example.com"
La entrada tiene espacios y mayúsculas para demostrar claramente qué transformación esperamos.
Para valores booleanos podemos usar is True o is False cuando queremos verificar exactamente un booleano:
from servicios import es_usuario_activo
def test_usuario_activo_devuelve_true():
usuario = {"nombre": "Ana", "activo": True}
assert es_usuario_activo(usuario) is True
def test_usuario_sin_activo_devuelve_false():
usuario = {"nombre": "Luis"}
assert es_usuario_activo(usuario) is False
El segundo caso confirma el valor por defecto cuando falta la clave activo.
Las listas pueden compararse directamente con assert:
from servicios import obtener_nombres
def test_obtener_nombres():
productos = [
{"nombre": "Teclado", "precio": 50000},
{"nombre": "Mouse", "precio": 12000},
]
resultado = obtener_nombres(productos)
assert resultado == ["Teclado", "Mouse"]
En esta comparación importa tanto el contenido como el orden.
También conviene probar colecciones vacías:
def test_obtener_nombres_con_lista_vacia():
resultado = obtener_nombres([])
assert resultado == []
Esta prueba confirma que la función no falla cuando no hay productos.
Para diccionarios podemos comparar toda la estructura esperada:
from servicios import crear_resumen_producto
def test_crear_resumen_producto():
resultado = crear_resumen_producto(" teclado ", 50000)
assert resultado == {
"nombre": "Teclado",
"precio": 50000,
"disponible": True,
}
Esta prueba verifica normalización del nombre, precio y disponibilidad.
Si queremos enfocarnos en una regla específica, podemos comprobar una sola clave:
def test_producto_con_precio_cero_no_esta_disponible():
resultado = crear_resumen_producto("mouse", 0)
assert resultado["disponible"] is False
Esta prueba se concentra en la regla de disponibilidad.
Para probar errores esperados usamos pytest.raises:
import pytest
from servicios import aplicar_cupon
def test_cupon_invalido_lanza_error():
with pytest.raises(ValueError):
aplicar_cupon(1000, "NO_EXISTE")
La prueba pasa si la función lanza ValueError.
Podemos guardar la excepción para revisar su mensaje:
def test_cupon_invalido_muestra_mensaje_claro():
with pytest.raises(ValueError) as error:
aplicar_cupon(1000, "NO_EXISTE")
assert str(error.value) == "Cupón inválido"
Conviene comprobar mensajes solo cuando forman parte importante del comportamiento esperado.
El archivo test_servicios.py puede quedar así:
import pytest
from servicios import (
aplicar_cupon,
calcular_total,
crear_resumen_producto,
es_usuario_activo,
normalizar_email,
obtener_nombres,
)
def test_calcular_total():
resultado = calcular_total(1000, 3)
assert resultado == 3000
def test_normalizar_email():
resultado = normalizar_email(" ANA@EXAMPLE.COM ")
assert resultado == "ana@example.com"
def test_usuario_activo_devuelve_true():
usuario = {"nombre": "Ana", "activo": True}
assert es_usuario_activo(usuario) is True
def test_usuario_sin_activo_devuelve_false():
usuario = {"nombre": "Luis"}
assert es_usuario_activo(usuario) is False
def test_obtener_nombres():
productos = [
{"nombre": "Teclado", "precio": 50000},
{"nombre": "Mouse", "precio": 12000},
]
resultado = obtener_nombres(productos)
assert resultado == ["Teclado", "Mouse"]
def test_obtener_nombres_con_lista_vacia():
resultado = obtener_nombres([])
assert resultado == []
def test_crear_resumen_producto():
resultado = crear_resumen_producto(" teclado ", 50000)
assert resultado == {
"nombre": "Teclado",
"precio": 50000,
"disponible": True,
}
def test_producto_con_precio_cero_no_esta_disponible():
resultado = crear_resumen_producto("mouse", 0)
assert resultado["disponible"] is False
def test_cupon_invalido_lanza_error():
with pytest.raises(ValueError):
aplicar_cupon(1000, "NO_EXISTE")
def test_cupon_invalido_muestra_mensaje_claro():
with pytest.raises(ValueError) as error:
aplicar_cupon(1000, "NO_EXISTE")
assert str(error.value) == "Cupón inválido"
Ejecuta:
python -m pytest
La salida esperada será similar a:
collected 10 items
test_servicios.py .......... [100%]
10 passed in 0.03s
Para ver cada prueba por nombre:
python -m pytest -v
La opción -v ayuda a revisar rápidamente qué casos fueron ejecutados.
Una prueba simple suele tener tres partes:
| Parte | Qué hace | Ejemplo |
|---|---|---|
| Preparación | Define datos de entrada | usuario = {"activo": True} |
| Ejecución | Llama a la función | resultado = es_usuario_activo(usuario) |
| Verificación | Comprueba el resultado | assert resultado is True |
Los nombres de las pruebas deben describir qué comportamiento se espera:
def test_usuario_sin_activo_devuelve_false():
usuario = {"nombre": "Luis"}
assert es_usuario_activo(usuario) is False
Este nombre permite entender la regla sin leer toda la implementación.
NameError.test_: pytest no la ejecutará.assert compara el orden de los elementos.mkdir pytest-pruebas-simples-demo
cd pytest-pruebas-simples-demo
python -m pip install pytest
python -m pytest
python -m pytest -v
python -m pytest test_servicios.py
pytest podemos escribir pruebas como funciones simples.test_.assert permite comparar números, cadenas, listas y diccionarios.pytest.raises permite probar excepciones esperadas.En este tema escribimos pruebas simples con pytest para distintos tipos de resultados. Practicamos assert, pytest.raises y nombres descriptivos.
En el próximo tema veremos con más detalle cómo funcionan las aserciones con assert y cómo interpretar las fallas que muestra pytest.