Cuando una prueba falla, el mensaje de falla es nuestra primera fuente de información. Leerlo bien ahorra tiempo y evita cambios impulsivos.
Un mensaje de falla puede indicar qué prueba falló, en qué archivo ocurrió, qué línea produjo el problema, qué valor se esperaba y qué valor se obtuvo.
En este tema aprenderemos a interpretar esos datos para diagnosticar con más precisión.
Un mensaje de falla suele incluir varias partes:
No todos los frameworks muestran la información igual, pero la mayoría contiene estas piezas.
Supongamos esta prueba:
def aplicar_descuento(precio):
return precio * 0.10
def test_aplicar_descuento_devuelve_precio_final():
assert aplicar_descuento(1000) == 900
Una salida posible sería:
FAILED test_descuentos.py::test_aplicar_descuento_devuelve_precio_final
assert 100.0 == 900
La prueba esperaba 900, pero obtuvo 100. El mensaje ya sugiere una diferencia de interpretación: la función devuelve el monto del descuento, no el precio final.
La primera información útil es el nombre del archivo y de la prueba.
test_descuentos.py::test_aplicar_descuento_devuelve_precio_final
Esto nos indica dónde empezar. El nombre de la prueba también comunica el comportamiento esperado. Si el nombre es claro, ya tenemos una pista antes de abrir el archivo.
La comparación entre esperado y obtenido es central.
assert 100.0 == 900
En este caso:
Algunos frameworks muestran primero el valor obtenido y luego el esperado; otros lo hacen al revés. Conviene conocer la herramienta, pero la diferencia entre ambos valores es lo más importante.
Ante una falla, no conviene cambiar la aserción solo para que pase. Primero debemos entender si la expectativa es correcta.
Preguntas útiles:
La falla no nos dice automáticamente qué cambiar. Nos dice dónde hay una diferencia.
Una falla por aserción ocurre cuando el código se ejecuta, pero el resultado no cumple la expectativa.
def test_sumar():
assert sumar(2, 3) == 6
Si sumar(2, 3) devuelve 5, la prueba falla. En este caso, probablemente la prueba está mal, porque la suma correcta es 5.
Este ejemplo muestra que una aserción fallida no siempre significa código productivo defectuoso.
Un error ocurre cuando la prueba no llega a completar su verificación porque aparece una excepción inesperada.
def test_total():
total = calcular_totl([100, 200])
assert total == 300
Salida posible:
NameError: name 'calcular_totl' is not defined
El problema no es el resultado 300. La prueba ni siquiera pudo llamar a la función correcta porque el nombre está mal escrito.
Una traza, o stack trace, muestra la cadena de llamadas que llevó al error. Puede parecer intimidante, pero normalmente debemos buscar las líneas que pertenecen a nuestro código.
File "test_calculadora.py", line 5, in test_dividir
resultado = dividir(10, 0)
File "calculadora.py", line 2, in dividir
return a / b
ZeroDivisionError: division by zero
La traza indica que la prueba llamó a dividir(10, 0) y que el error ocurrió dentro de calculadora.py al intentar dividir por cero.
En trazas largas, no siempre conviene leer todo desde el comienzo. Una estrategia práctica es buscar:
Las líneas de librerías o frameworks pueden ser útiles, pero muchas veces solo muestran el camino hasta llegar a nuestro código.
Cuando fallan listas o diccionarios, los frameworks suelen mostrar diferencias.
assert [2, 4, 6] == [2, 4]
Left contains one more item: 6
El mensaje indica que el resultado obtenido tiene un elemento adicional. Debemos revisar si la función está devolviendo de más o si la expectativa de la prueba está incompleta.
Las fallas con textos pueden deberse a mayúsculas, espacios, acentos o formato.
assert ' Ana ' == 'Ana'
- Ana
+ Ana
El resultado contiene espacios extra. El mensaje ayuda a ver una diferencia que visualmente puede pasar desapercibida.
A veces la suite falla antes de ejecutar una prueba porque no puede importar un módulo o función.
ImportError: cannot import name 'calcular_total' from 'compras'
Esto puede indicar:
En estos casos, la prueba no llegó a verificar comportamiento. Primero hay que corregir el problema de importación.
Una prueba puede fallar porque los datos de preparación no representan el caso que el nombre promete.
def test_cliente_vip_recibe_descuento():
cliente = Cliente(tipo="comun")
descuento = calcular_descuento(cliente, 1000)
assert descuento == 150
El nombre dice cliente VIP, pero el dato preparado es cliente común. El problema está en la prueba, no necesariamente en el código productivo.
A veces una prueba falla porque el requisito cambió y la prueba todavía expresa la regla anterior. En ese caso, debemos actualizar la prueba con cuidado.
Antes de cambiarla, conviene confirmar:
No se debe modificar una prueba solo porque falló; se modifica si la expectativa anterior dejó de ser correcta.
Una falla intermitente es una prueba que a veces pasa y a veces falla sin cambios aparentes. Este tipo de falla es especialmente dañino porque reduce la confianza en la suite.
Causas frecuentes:
Cuando una falla es intermitente, debemos buscar fuentes de no determinismo.
Los mensajes personalizados pueden ayudar si explican la regla que se incumplió.
def test_cliente_vip_recibe_descuento():
descuento = calcular_descuento("vip", 1000)
assert descuento == 150, "Un cliente VIP debe recibir 15% de descuento"
El mensaje no reemplaza un buen nombre de prueba, pero puede aportar contexto cuando la regla no es obvia.
| Mensaje observado | Posible causa | Primer paso |
|---|---|---|
assert 100 == 900 |
Resultado distinto al esperado. | Revisar regla, código y expectativa. |
NameError |
Nombre inexistente o mal escrito. | Revisar llamada o importación. |
ImportError |
Módulo o función no encontrada. | Revisar estructura del proyecto. |
| Diferencia en lista | Elementos faltantes, extra u orden incorrecto. | Comparar contenido esperado y obtenido. |
| Falla intermitente | No determinismo. | Buscar tiempo, orden, datos compartidos o red. |
Al interpretar una falla, revisa:
Interpretar mensajes de falla es una habilidad esencial para trabajar con pruebas unitarias. Una falla bien leída nos guía hacia la causa; una falla mal interpretada puede llevarnos a cambiar lo que no corresponde.
El objetivo es leer con método: identificar la prueba, comparar esperado contra obtenido, revisar la traza y decidir si el problema está en el código, en la prueba o en el requisito.
En el próximo tema veremos independencia entre pruebas, un principio clave para evitar fallas difíciles de diagnosticar.