Le pasás una función a un modelo, le pedís tests, y en segundos tenés veinte casos en verde y una cobertura que sube. Da una tranquilidad engañosa. Una prueba sirve si puede fallar cuando el código está mal, y las que genera un modelo a partir de la implementación muchas veces no cumplen esa condición.
El problema de los tests espejo
Tomemos una función simple:
def precio_final(precio, descuento):
return precio - precio * descuento / 100
Si le pedís a un modelo que la pruebe mirando solo el código, es habitual que escriba algo así:
def test_precio_final():
precio, descuento = 200, 10
assert precio_final(precio, descuento) == precio - precio * descuento / 100
Pasa, claro: repite la fórmula. Si mañana alguien rompe la función con un error en la fórmula, este test se rompe, pero si la fórmula original ya estaba mal (por ejemplo, porque el negocio calcula el descuento sobre otra base), el test lo confirma con la misma convicción. Compará con esto:
def test_precio_final():
assert precio_final(200, 10) == 180
assert precio_final(100, 0) == 100
assert precio_final(100, 100) == 0
Acá los valores esperados salen de afuera del código: alguien hizo la cuenta o los dijo el cliente. Eso es lo que convierte una prueba en una especificación.
Quién define el comportamiento esperado
Es la pregunta de fondo. El modelo puede deducir qué hace el código, pero no qué debería hacer. Eso lo saben las personas: el requerimiento, la conversación con el cliente, la norma que aplica. Cuando delegás la escritura de tests sin dar esa información, le estás pidiendo que describa el código, no que lo verifique.
Una forma práctica de resolverlo es darle al modelo el requerimiento en lugar del código, o ambos, y pedirle que escriba las pruebas a partir de lo que debería pasar. Después revisás que cada valor esperado coincida con lo que vos sabés del dominio.
Qué sí conviene delegar
- Datos de ejemplo. Armar una lista de clientes ficticios, fechas, direcciones o textos con acentos y caracteres raros es tedioso y el modelo lo hace bien. Revisá que no incluya datos reales de alguien.
- Propuestas de casos borde. Pedile una lista de situaciones límite para una función: valores vacíos, negativos, cero, números enormes, cadenas con espacios, fechas de fin de mes o de año bisiesto. Aunque no todas apliquen, sirve para detectar casos que no habías pensado.
- Estructura repetitiva. Pruebas parametrizadas, preparación de datos, mocks sencillos.
- Esqueleto de tests para código heredado. Antes de refactorizar algo sin pruebas, un conjunto inicial que fije el comportamiento actual ayuda, siempre que sepas que ese comportamiento puede incluir errores y que lo tomás solo como red de seguridad.
Qué revisar siempre
Que la prueba falle cuando debe. Es el control más barato y el que más se saltea. Rompé a propósito el código (cambiá un operador, quitá una condición) y comprobá que algún test se ponga en rojo. Si todo sigue en verde, ese test no protege nada. Existen herramientas que automatizan esta idea, llamada pruebas de mutación, pero hacerlo a mano en una función ya enseña mucho.
Que los valores esperados sean correctos. No los des por buenos porque están escritos con seguridad.
Que no dependan de detalles internos. Los tests que verifican cómo se hace algo (qué función auxiliar se llama, en qué orden) y no qué resultado se obtiene se rompen con cualquier refactor y frenan el cambio.
Que no se burlen de todo. Un test donde se reemplaza cada dependencia por un doble termina probando los dobles.
Cobertura no es calidad
Un porcentaje alto de cobertura indica que las líneas se ejecutaron durante las pruebas, no que alguien verificó su resultado. Un test sin aserciones suma cobertura y no comprueba nada. Los modelos producen tests en cantidad con facilidad, y esa facilidad empuja a mirar el número en vez del contenido.
Tomá la cobertura como una pista sobre qué zonas nunca se ejecutaron, y la calidad como algo que se juzga leyendo y rompiendo el código a propósito. Si tenés que elegir entre diez tests generados que no revisaste y tres que entendés y sabés que fallan cuando deben, quedate con los tres.