TestGen-LLM de Meta para mejorar las pruebas unitarias automatizadas
- La herramienta TestGen-LLM, desarrollada por Meta, utiliza modelos de lenguaje de gran escala (LLM) para mejorar automáticamente pruebas escritas por humanos.
- Las clases de prueba generadas por TestGen-LLM superan con éxito una serie de filtros que garantizan mejoras medibles frente a la suite de pruebas original, resolviendo problemas de alucinación de LLM.
- Se describe el despliegue de TestGen-LLM en los test-a-thons de Meta para las plataformas de Instagram y Facebook.
Desempeño de TestGen-LLM
- En la evaluación de los productos Reels y Stories de Instagram, el 75% de los casos de prueba generados por TestGen-LLM se compilaron correctamente, el 57% se ejecutó con fiabilidad y el 25% aumentó la cobertura.
- En los test-a-thons de Instagram y Facebook de Meta, TestGen-LLM mejoró el 11.5% de todas las clases donde se aplicó, y los ingenieros de software de Meta aceptaron el 73% de las recomendaciones para el despliegue.
- Este es el primer informe de despliegue industrial a gran escala de código generado por LLM con este tipo de garantía de mejora de código.
Opinión de GN⁺
- TestGen-LLM puede ser una herramienta con potencial de innovación en la automatización y mejora de la calidad del software de pruebas, al lograr mejoras al aprovechar modelos de lenguaje de gran escala para mejorar pruebas existentes.
- Esta herramienta contribuye de manera importante a la comunidad de ingeniería de software al aumentar la cobertura de pruebas en un entorno industrial y generar casos de prueba fiables.
- La aplicación exitosa en los test-a-thons de Meta muestra que TestGen-LLM puede integrarse al desarrollo de productos reales, lo que puede mejorar significativamente la eficiencia y la estabilidad del desarrollo de software.
1 comentarios
Comentarios en Hacker News
En una gran aseguradora donde trabajé antes, la dirección puso como meta 80% de cobertura de pruebas para toda la base de código, y la gente empezó a escribir pruebas unitarias inútiles para los getters/setters de los DTO de Java solo para cumplir la meta
Obviamente, los desarrolladores tampoco podían cambiar las reglas de medición de cobertura de Sonar, y de joven aprendí que si solo miras los KPI, puedes inducir comportamientos que no coinciden con la intención original
Es muy probable que unos cuantos escenarios de pruebas E2E bien diseñados hubieran sido mejores para la calidad del software
El problema era que el código heredado y desordenado estaba muy bien probado, con 95% de cobertura. Mi código nuevo tenía 100% de cobertura, pero como era mucho más corto, la cobertura total en realidad bajó y no pasó
Lo único que quedaba era código de UI en Swing, difícil de probar y con poco valor para probar, así que el líder de desarrollo, en vez de dedicar 1 o 2 semanas a escribir pruebas de Swing, dejó el código viejo en algún lugar del repositorio e hizo que las pruebas siguieran apuntando a ese código
Al final, miles de líneas de código muerto que nunca se llamarían en producción se quedaron en el repositorio solo para satisfacer a Sonar
El resultado fue que aparecieron miles de constantes en los headers como
static const unsigned ONE = 1;,TWO = 2;,THREE = 3;https://en.m.wikipedia.org/wiki/Mutation_testing
Hay herramientas y frameworks para casi todos los lenguajes; por ejemplo stryker-mutator (C#, TypeScript), pitest (Java), mutatest (Python)
En 6 años de experiencia, nunca había visto una aplicación tan mal escrita; no solo por estilo, sino porque también había muchas partes realmente rotas, y nadie sabía qué estaba mal
De verdad odio Sonar. Debería usarse solo para reportar vulnerabilidades; no para decirme que cambie nombres de variables o que “debo refactorizar este código duplicado”. Ya existe un backlog de tickets en Jira; no hace falta que también me diga qué hacer y cuándo hacerlo
Pero a los gerentes les encantan este tipo de herramientas para jugar al poder
El gran problema es que lo vuelven obligatorio y, para evitar una tontería, te hacen pasar por un enorme proceso burocrático. La semana pasada incluso discutí porque una herramienta obligatoria de calidad de código se quejaba de que
res.status(200).json()no tenía header HSTSSeguía quejándose tanto si lo configuraba manualmente como si usaba
app.use(helmet()), y al final parecía querer que escribiéramos todo el backend en un solo archivo. Cuando en realidad HSTS se maneja de forma más elegante y automática en el ingress o en el balanceador de cargaPodría haber marcado eso como falso positivo y pasar 1 o 2 semanas explicándole a un gerente de nivel superior qué es HSTS para conseguir la aprobación, pero al final terminé agregando
res.sendJson(data, status = 200)al prototipo del objeto de respuesta. Claramente es una implementación tonta, pero te hace darte cuenta de que, en sectores muy burocráticos, el mal software se construye como la suma de estas malas implementacionesAl ver la afirmación de que “el 75% de los casos de prueba de TestGen-LLM compiló correctamente, el 57% pasó de forma estable y el 25% aumentó la cobertura”, el problema es que parece bastante probable que las pruebas generadas por LLM “certifiquen” comportamientos con errores.
Sobre todo, parece aún más probable en codebases que ya tienen baja cobertura de pruebas. Si una persona escribe una prueba nueva directamente, existe la ventaja de que hay alguien que puede juzgar si el sistema es tonto o si la prueba está mal.
Como mínimo, este tipo de pruebas debería separarse en una carpeta especial y tratarse con un nivel adecuado de escepticismo
Aun así, una codebase con buena cobertura permite hacer grandes refactorizaciones de forma segura y sin regresiones, y eso sigue siendo una propiedad útil incluso si hay un bug y la refactorización conserva ese bug tal cual.
El riesgo de una herramienta de generación de pruebas diseñada para codificar el comportamiento actual es que uno puede caer en una falsa sensación de seguridad cuando en realidad solo codificó el comportamiento actual.
Tal vez esto se resolvería si no lo llamáramos “pruebas”, sino algo como “snapshots de comportamiento”. El nombre debería transmitir que capturan el comportamiento actual, no el comportamiento correcto.
Por eso creo que siempre se necesita cierto grado de supervisión humana para determinar qué escenario ocurrió.
Esto pasa en toda clase de sistemas, y la gente tiende a pensar que se resuelve agregando otra capa de automatización, como aquí. Las pruebas se inventaron originalmente para verificar si el programa funciona correctamente, pero si hasta eso se automatiza, volvemos a encontrarnos con el mismo problema a mayor escala, es decir, en forma de pruebas en lugar de assertions.
Puede que no se sepa cómo crear factories para las entradas secundarias necesarias para las pruebas, pero sí se sabe cómo debería funcionar el código en sí.
Si un LLM puede armar esa base de pruebas y facilitar que el desarrollador escriba la validación de la lógica de negocio, eso puede ser una gran ventaja.
Aun así, si las pruebas generadas quedan demasiado acopladas a la implementación, como pasa con la mayoría de las pruebas unitarias, terminarán ralentizando el desarrollo. Si corregir pruebas individuales se vuelve demasiado difícil, incluso podría verse a gente borrando todas las pruebas y regenerándolas en cambios grandes.
Puede que parte del código dependa de ese bug, y corregirlo, ya sea por accidente o a propósito, podría causar un problema más grave.
Por supuesto, ese tipo de pruebas no puede reemplazar a las pruebas que validan los requisitos reales.
Pero hay incontables sistemas legacy en modo de mantenimiento con baja cobertura, y en esos casos generar pruebas que validen el comportamiento actual es muy útil. Permite comprobar que, cuando alguien hace un cambio, todo lo demás sigue igual.
Después de leer el PDF, esto parece generar pruebas que “simplemente” pasan repetidamente, es decir, que no son inestables.
El objetivo principal es crear un conjunto de pruebas de regresión con pruebas que fijen el comportamiento del código existente, no reemplazar las pruebas escritas por desarrolladores que conocen los requisitos funcionales.
Hace casi 20 años, en una empresa donde trabajé, también probamos AgitarOne, que prometía generar automáticamente casos de prueba para explorar el comportamiento del código Java. Además, podía producir pruebas que pasaban casi de forma automática para usarlas como conjunto de pruebas de regresión.
En lo personal no me gustó. Generaba demasiadas cosas y la gerencia entendía que si subía la cobertura, también subía la calidad. Me pregunto cuánto mejor es el enfoque con LLM que menciona FB frente a aquello.
http://www.agitar.com/solutions/products/agitarone.html
Hasta que un LLM pueda juzgar la corrección real sin depender del supuesto o del oráculo de que las buenas pruebas pasan, parece difícil llegar más lejos que esto. De una forma u otra, habría que incluir expectativas de comportamiento en el prompt.
El valor de las pruebas está en garantizar que no se rompa algo que a alguien le importa, no en congelar para siempre todos los comportamientos de casos límite poco usados que solo son un subproducto de una implementación específica.
Por experiencia, escribir pruebas suele ser una excelente manera de evaluar la calidad del código.
Si las pruebas son complejas o es difícil lograr cobertura, es muy probable que el código bajo prueba necesite mejoras.
El código con bajo acoplamiento, alta cohesión y baja complejidad debería ser fácil de probar unitariamente.
Se dice que, en la evaluación de productos de Reels y Stories de Instagram, el 75% de los casos de prueba de TestGen-LLM compiló correctamente, el 57% pasó de forma estable y el 25% aumentó la cobertura.
En eventos de pruebas de Instagram y Facebook de Meta, mejoró el 11.5% del total de clases aplicadas, y el 73% de las recomendaciones fue aprobado por ingenieros de software de Meta para desplegarse a producción.
No sé si son buenos porcentajes. Habría que leer más para saber si lo que no se aceptó eran errores menores que normalmente detectamos en code review o problemas graves. Si un ingeniero humano tuviera una tasa de fallo del 25%, dependiendo del tipo de fallo, podría no ser de mucha ayuda.
También dudo si toda la misión de automatizar la generación de pruebas unitarias para código Android va en una buena dirección. La gente de TDD probablemente se esté revolcando en sus tumbas o, bueno, en sus camas en casa. Aunque supongo que igual habrán puesto condiciones al respecto.
Durante un tiempo, el enfoque principal en unlogged.io fue la generación automática de pruebas JUnit, pero no logró despegar por varias razones
El código de prueba generado era demasiado abundante como para que los desarrolladores quisieran mantenerlo, no podía simular escenarios reales y la cobertura de código era una métrica de vanidad. Los desarrolladores encontraban formas de rodear el objetivo con escenarios sin sentido
Ahora están trabajando para simular todos los escenarios únicos de producción y ofrecer pruebas de reproducción sin código que los desarrolladores puedan volver a ejecutar localmente con las dependencias externas mockeadas
Por cierto, soy el fundador de unlogged.io
Quiero ir en la dirección contraria. Si ingresas los criterios de aceptación, quiero que genere pruebas que los verifiquen y luego que genere el código que haga pasar esas pruebas
A veces se puede lograr algo parecido de forma limitada con Copilot, pero no sé por qué siento que nadie parece enfocarse en este orden
TestGen-LLM es una creación realmente extraña. Parece que podría servir como primer paso para una refactorización o reescritura, pero que el paper enfatice la cobertura de código da la impresión de tener el juicio completamente desviado
Si la organización ya está rota por exigir alta cobertura, quizá podría servir, pero TestGen-LLM no hará que el código del proyecto mejore de ninguna manera y solo aumentará la fricción para implementar mejoras reales
Sería mucho más útil generar pruebas para casos límite que podrían pasar o no, pero TestGen-LLM depende de filtrar la basura del LLM mediante errores de compilación y pruebas fallidas
El hecho de que el paper no incluya ni un solo ejemplo de prueba generada me hace sospechar que probablemente sea igual de amateur que otro código generado por LLM que he visto hasta ahora
Incluso ayudó a entender qué intentaba hacer el código
Es interesante que sea un paper de 12 páginas escrito por empleados de Meta para promocionar IA para desarrolladores, y hasta sacaron un diagrama de Sankey
Puede que me equivoque, pero si lo van a presentar así, ¿no deberían proporcionar también información reproducible?
No es una teoría conspirativa; simplemente no tengo datos al nivel de los que Meta usa para entrenamiento. Me pregunto si habrán publicado algo
Me pregunto cuánto costará mantener en el futuro un corpus gigantesco de pruebas generadas automáticamente
No solo deberían ofrecer la generación de casos de prueba, sino también métodos automatizados para actualizarlos