26 readsTesting · Fintech · Calidad

El test que convertía un bug en especificación

El test que convertía un bug en especificación

Un monto llegó como "1.500" y el sistema lo guardó como 1500.

Mil quinientos en vez de uno con cinco. Un error de exactamente ×1000, en un camino por donde pasa dinero real.

Lo que convierte esto en una historia que vale la pena contar no es el bug. Es lo que encontré cuando fui a arreglarlo: un test unitario, verde, que afirmaba que "1.500" debía convertirse en 1500.

El bug no se había colado a pesar de las pruebas. Estaba protegido por ellas.

El helper que adivinaba

La función era un normalizador de decimales. Recibía cadenas de distintas procedencias —formularios, respuestas de proveedores, importaciones— y devolvía un número. Como las cadenas llegaban en formatos mezclados, alguien le enseñó a deducir la intención:

  • Si hay coma y punto, el último es el decimal.
  • Si solo hay coma, es decimal (formato europeo).
  • Si solo hay punto y le siguen exactamente tres dígitos, es separador de miles.

Esa última regla es la que mata. Es razonable en abstracto: "1.500" en español significa mil quinientos, y quien la escribió estaba pensando en eso. El problema es que "1.500" en inglés significa uno con cinco, y ambos formatos entraban por el mismo sitio.

No hay forma de resolver la ambigüedad mirando la cadena. Ninguna. "1.500" no contiene la información necesaria para saber qué quiso decir quien lo escribió. Es un dato incompleto, y el helper respondía con una certeza que no tenía.

Por qué el test estaba verde

Cuando escribes un test después de escribir el código, no estás verificando que el código sea correcto. Estás verificando que hace lo que hace.

Es una distinción sutil y tiene consecuencias enormes. El autor del helper ejecutó mentalmente su función, vio que "1.500" producía 1500, pensó "sí, mil quinientos, correcto" y escribió la aserción. El test pasó a la primera. Se sintió productivo.

A partir de ese momento, ese comportamiento quedó congelado. Cualquiera que en el futuro intentara corregirlo rompería una prueba, y romper una prueba se siente como haber cometido un error. La reacción natural no es "este test está mal", es "he roto algo, déjame revertir".

Un test no distingue entre una regla de negocio y un accidente de implementación. Solo sabe que ayer devolvía esto y hoy devuelve aquello. Somos nosotros quienes le damos autoridad moral, y se la damos entera.

La pregunta que faltaba

El problema de fondo no era el parseo. Era que alguien tuvo que adivinar porque el formato no viajaba con el dato.

Un monto sin su contexto no es un monto: es una cadena que se le parece. "1.500" no significa nada por sí solo. Necesitas saber la moneda, y sobre todo cuántos decimales tiene esa moneda: dos para el dólar, cero para el yen, tres para el dinar.

Por eso, en cualquier sistema que mueva dinero, la representación tiene que ser explícita en tres partes:

  • El valor, como entero, en la unidad mínima. 1500 centavos, no 15.00 dólares. Los enteros no tienen sorpresas de coma flotante.
  • El activo. USD, VES, BTC.
  • El exponente. Cuántos decimales aplica ese activo.

Con esas tres piezas, 1500 / USD / 2 es inequívoco: quince dólares. No hay nada que deducir. Y lo más importante: si alguien te entrega una cadena sin ese contexto, el sistema puede rechazarla en vez de inventarse la respuesta.

Esa es la diferencia entre un parser y un contrato. El parser intenta entenderte. El contrato te obliga a ser claro.

Lo que cambié, y en qué orden

Primero borré el test.

Suena mal escrito así, pero era el paso correcto y tenía que ir primero: mientras esa aserción existiera, cualquier corrección parecería una regresión. Un test que codifica un comportamiento incorrecto no es una red de seguridad, es una trampa. Se elimina, y se escribe en su lugar el que describe lo que el sistema debe hacer.

El nuevo dice que "1.500", sin contexto de formato, lanza una excepción. No devuelve 1500. No devuelve 1.5. Se niega.

Después vino lo demás: el formato explícito en los bordes donde entra el dato, dinero como entero con activo y exponente hacia dentro, y ningún punto del sistema donde una cadena ambigua se convierta en un número en silencio.

La pregunta que hago ahora

Cuando reviso una suite de tests, ya no me tranquiliza verla verde. Verde solo significa que el sistema sigue comportándose como se comportaba.

La pregunta que hago es otra:

¿Cuáles de estos tests fueron escritos antes del código, y cuáles después de mirar lo que el código ya hacía?

Los segundos no verifican nada. Documentan. Y si el código estaba mal el día que los escribiste, ahora tienes el bug con certificado de calidad, un candado encima, y un compañero que dentro de un año no se atreverá a tocarlo.

Share

Comments

No comments yet. Be the first.

Never published. I only use it to reply to you.

Comments are reviewed before they appear.