22. Cómo interpretar un mensaje de falla

22.1 Introducción

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.

22.2 Partes de un mensaje de falla

Un mensaje de falla suele incluir varias partes:

  • Nombre de la prueba fallida.
  • Archivo donde está la prueba.
  • Línea que falló.
  • Tipo de error o falla.
  • Comparación entre esperado y obtenido.
  • Traza de llamadas, si hubo una excepción.

No todos los frameworks muestran la información igual, pero la mayoría contiene estas piezas.

22.3 Ejemplo simple de falla

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.

22.4 Ubicar la prueba

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.

22.5 Leer esperado y obtenido

La comparación entre esperado y obtenido es central.

assert 100.0 == 900

En este caso:

  • Obtenido: 100.0.
  • Esperado: 900.

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.

22.6 No corregir antes de entender

Ante una falla, no conviene cambiar la aserción solo para que pase. Primero debemos entender si la expectativa es correcta.

Preguntas útiles:

  • ¿Qué decía la regla original?
  • ¿La prueba espera el comportamiento correcto?
  • ¿El código implementa otra regla?
  • ¿El nombre de la función coincide con lo que devuelve?
  • ¿La prueba preparó bien los datos?

La falla no nos dice automáticamente qué cambiar. Nos dice dónde hay una diferencia.

22.7 Falla por aserción

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.

22.8 Error por excepción inesperada

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.

22.9 Leer la traza

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.

22.10 Leer desde la parte más relevante

En trazas largas, no siempre conviene leer todo desde el comienzo. Una estrategia práctica es buscar:

  • La primera línea que menciona nuestro archivo de prueba.
  • La línea de nuestro código productivo donde ocurrió el error.
  • El tipo de excepción al final.
  • El mensaje asociado a esa excepción.

Las líneas de librerías o frameworks pueden ser útiles, pero muchas veces solo muestran el camino hasta llegar a nuestro código.

22.11 Diferencias en colecciones

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.

22.12 Diferencias en textos

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.

22.13 Errores de importación

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:

  • La función no existe.
  • El nombre cambió.
  • El archivo está en otra ubicación.
  • La configuración del proyecto no permite encontrar el módulo.

En estos casos, la prueba no llegó a verificar comportamiento. Primero hay que corregir el problema de importación.

22.14 Fallas por preparación incorrecta

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.

22.15 Fallas por cambio de requisito

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:

  • ¿El nuevo requisito está claro?
  • ¿La prueba antigua ya no representa un comportamiento válido?
  • ¿Hay otras pruebas relacionadas que también deban actualizarse?
  • ¿El nombre de la prueba sigue siendo correcto?

No se debe modificar una prueba solo porque falló; se modifica si la expectativa anterior dejó de ser correcta.

22.16 Fallas intermitentes

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:

  • Dependencia del tiempo actual.
  • Orden de ejecución entre pruebas.
  • Datos compartidos modificables.
  • Dependencias externas.
  • Aleatoriedad no controlada.

Cuando una falla es intermitente, debemos buscar fuentes de no determinismo.

22.17 Mensajes personalizados

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.

22.18 Tabla de diagnóstico

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.

22.19 Lista de comprobación

Al interpretar una falla, revisa:

  • ¿Qué prueba falló?
  • ¿En qué archivo y línea ocurrió?
  • ¿Fue una aserción fallida o una excepción inesperada?
  • ¿Cuál era el valor esperado?
  • ¿Cuál fue el valor obtenido?
  • ¿La preparación del caso coincide con el nombre de la prueba?
  • ¿La regla cambió o el código está mal?

22.20 Qué debes recordar de este tema

  • Un mensaje de falla contiene información para diagnosticar.
  • Debemos ubicar prueba, archivo y línea antes de corregir.
  • Una aserción fallida indica diferencia entre esperado y obtenido.
  • Una excepción inesperada puede indicar error de preparación, importación o código.
  • No toda falla significa que el código productivo está mal.
  • Las trazas deben leerse buscando las líneas de nuestro código.
  • Las fallas intermitentes deben investigarse con prioridad.

22.21 Conclusión

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.