Aserciones y Estructura de Pruebas
Una estructura de pruebas clara hace que los fallos sean diagnosticables en segundos. Las sentencias assert sencillas de pytest, junto con el patrón arrange-act-assert, son la base de las pruebas legibles en Python.
Receta
def test_discount_applied():
# Arrange (Organizar)
price = Decimal("100.00")
# Act (Actuar)
result = apply_discount(price, pct=10)
# Assert (Aseverar)
assert result == Decimal("90.00")Cuándo usar esto:
- Las pruebas son difíciles de leer o depurar
- Los fallos muestran mensajes poco útiles
- Los revisores no pueden saber qué comportamiento se está probando
Ejemplo de Trabajo
from decimal import Decimal
import pytest
from myapp.billing import apply_discount, DiscountError
class TestApplyDiscount:
def test_ten_percent_off(self):
price = Decimal("50.00")
result = apply_discount(price, pct=10)
assert result == Decimal("45.00")
def test_zero_percent_unchanged(self):
price = Decimal("50.00")
assert apply_discount(price, pct=0) == price
def test_invalid_percent_raises(self):
with pytest.raises(DiscountError, match="pct must be 0-100"):
apply_discount(Decimal("10"), pct=-1)Lo que esto demuestra:
- Flujo arrange-act-assert en cada prueba
- Un comportamiento por función de prueba
pytest.raisespara probar excepciones con coincidencia de mensajes- La agrupación en clases es opcional (pytest recopila métodos
test_*)
Profundización
Introspección de Aserciones
pytest reescribe assert para mostrar valores en caso de fallo:
assert result == expected
# AssertionError: assert Decimal('45.00') == Decimal('50.00')Aserciones Útiles
| En lugar de | Usa |
|---|---|
assert x == True | assert x |
assert len(items) == 0 | assert not items |
assert type(x) == str | assert isinstance(x, str) |
pytest.raises(Error) solo | pytest.raises(Error, match="...") |
Trampas Comunes
- Múltiples aserciones por prueba - no está claro cuál falló. Solución: un comportamiento lógico por prueba; dividir casos.
- Aserción sin mensaje en comprobaciones complejas - fallo opaco. Solución:
assert x in items, f"{x} not in {items}". - Probar detalles de implementación - pruebas frágiles. Solución: aseverar salidas y comportamiento público, no llamadas internas.
- Sin limpieza después de organizar - el estado se filtra entre pruebas. Solución: usar fixtures para configuración/desmontaje.
- Comparar flotantes con == - fallos inestables. Solución:
pytest.approx(3.14, rel=1e-6).
Alternativas
| Alternativa | Úsala cuando | No la uses cuando |
|---|---|---|
| Aserciones de unittest | Solo stdlib | Quieres introspección |
| hypothesis | Comprobaciones basadas en propiedades | Unidades de lógica simple |
| Pruebas de snapshot | Estructuras de salida complejas | Unidades de lógica pura |
Preguntas Frecuentes
¿Qué es arrange-act-assert?
Configura los datos (arrange), llama al código bajo prueba (act), verifica el resultado (assert).
¿Debo usar clases de prueba?
Opcional. Las clases agrupan pruebas relacionadas; las funciones están bien para módulos simples.
¿Cómo asevero excepciones?
with pytest.raises(ValueError): o pytest.raises(ValueError, func, arg).
¿Cómo comparo flotantes?
assert result == pytest.approx(3.14).
¿Puedo usar assert simple?
Sí. pytest mejora automáticamente los fallos de assert. No necesitas self.assertEqual.
¿Cuánto debe durar una prueba?
Idealmente 5-15 líneas. Si es más larga, extrae la configuración en fixtures.
¿Los nombres de las pruebas deben describir el comportamiento?
Sí. test_discount_over_100_raises no test_discount_3.
¿Cómo asevero advertencias?
with pytest.warns(DeprecationWarning):.
¿Qué pasa con la aseveración de None?
assert result is None (identidad), no == None.
¿Cómo pruebo diccionarios aproximados?
Compara claves individualmente o usa pytest.approx en valores numéricos.
Relacionado
- Configuración de pytest - configuración del proyecto
- Fixtures - reutilización del paso arrange
- Parametrización - entradas múltiples
- Mejores Prácticas de Pruebas - convenciones
Versiones de la pila: Esta página fue escrita para Python 3.14.0, FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, y uv 0.6+.