Por Devy · Categoría: AI Dev Tools — Open Source
Pedile a un coding agent “agregá unit tests a este proyecto” y acabás de entregarle un problema mucho más ambiguo de lo que parece. ¿Qué código necesita tests? ¿Qué framework usa el repo? ¿Dónde viven los tests existentes? ¿Qué dependencias hay que mockear? ¿Qué casos realmente importan? ¿Y cómo sabemos que los tests nuevos prueban comportamiento real en vez de simplemente pasar?
Microsoft decidió que ese problema merecía un agente dedicado.
code-testing-generator es un agente open source y polyglot para generación de unit tests. No intenta implementar features, arreglar tickets, refactorizar el proyecto y actualizar la documentación al mismo tiempo. Tiene una misión mucho más estrecha:
encontrar qué necesita tests, entender cómo se testea ese repositorio, escribirlos y demostrar que funcionan.
Y justamente por eso resulta interesante.
La obsesión actual es construir agentes cada vez más generales. Microsoft está explorando el camino contrario: una responsabilidad estrecha, herramientas específicas y un resultado que se puede verificar automáticamente.
Documentación oficial de code-testing-generator
Repositorio microsoft/testfx
No es simplemente un prompt para “generar tests”
Microsoft describe code-testing-generator como un agente coordinador que ejecuta un pipeline completo.
El flujo básico es:
Research
↓
Plan
↓
Implement
↓
Build
↓
Test
↓
Fix
↓
Lint
↓
Validate
Y acá aparece la primera diferencia importante frente a pedirle tests a un chatbot.
Antes de escribir nada, el sistema investiga el repositorio.
Necesita descubrir cosas como:
- qué lenguaje está usando;
- cuál es el framework de testing;
- cómo está organizado el proyecto;
- dónde viven los tests existentes;
- qué convenciones siguen;
- qué código todavía necesita cobertura;
- qué dependencias externas deberían aislarse;
- cómo ejecutar correctamente build y tests.
Después construye un plan.
Sólo entonces empieza a generar código. (GitHub)
En realidad no es un agente: es un pequeño equipo
El nombre code-testing-generator puede hacer pensar que hay un único agente escribiendo tests.
La implementación es bastante más interesante.
Microsoft divide el trabajo entre agentes especializados:
code-testing-generator
│
├── code-testing-researcher
├── code-testing-planner
├── code-testing-implementer
├── code-testing-builder
├── code-testing-tester
├── code-testing-fixer
└── code-testing-linter
El generator coordina el pipeline.
El researcher entiende el codebase.
El planner decide qué tests implementar.
El implementer los escribe.
El builder comprueba que el proyecto compile.
El tester ejecuta los tests.
El fixer intenta corregir errores.
Y el linter deja el código consistente con las convenciones del proyecto. (GitHub)
Esto importa porque muestra un patrón bastante útil para cualquiera que esté construyendo agentes internos.
En lugar de:
SUPER AGENT
│
├── entiende el repo
├── diseña tests
├── escribe código
├── compila
├── depura
├── ejecuta tests
└── decide si terminó
Microsoft separa responsabilidades y convierte cada fase en un paso explícito.
No necesitás que un único prompt haga magia.
Necesitás un workflow que pueda comprobar su propio trabajo.
El loop que hace que testing sea perfecto para agentes
Hay tareas donde evaluar la respuesta de un agente es difícil.
Pedile:
Mejorá la arquitectura de este proyecto.
Puede producir 2.000 líneas de cambios y todavía necesitás un senior engineer para determinar si realmente mejoró algo.
Testing tiene una propiedad completamente distinta.
Podemos cerrar el loop:
analizar
↓
generar test
↓
compilar
↓
ejecutar
↓
¿funciona?
├── no → corregir
└── sí → validar
El entorno produce feedback automáticamente.
Eso no significa que un test que pasa sea necesariamente un buen test. Microsoft reconoce explícitamente ese problema: el agente también revisa las assertions, los escenarios solicitados y si el runner normal del repositorio descubre realmente los tests nuevos. (Microsoft for Developers)
La distinción es fundamental.
Un agente podría generar esto:
expect(true).toBe(true)
El test pasa.
No prueba absolutamente nada.
La métrica útil no puede ser simplemente:
tests verdes
Tiene que acercarse más a:
tests verdes
+
comportamiento relevante verificado
+
assertions significativas
+
integración correcta con el test suite existente
Qué testea —y qué no—
code-testing-generator está enfocado específicamente en unit tests.
Microsoft dice que el agente intenta aislar el código bajo prueba y mockear servicios externos u otras dependencias cuando corresponde. (Microsoft for Developers)
Por ahora quedan fuera del scope:
- integration tests;
- end-to-end tests;
- browser tests;
- performance tests.
Eso no es una carencia accidental.
Es parte de la idea.
Los unit tests proporcionan un entorno relativamente controlado donde el agente puede generar una hipótesis sobre comportamiento y comprobarla rápidamente.
Cuanto más se aleja de ese entorno —browser state, servicios externos, infraestructura, timing distribuido— más difícil se vuelve cerrar automáticamente el loop.
Cómo probarlo
Hay una aclaración importante antes de empezar: code-testing-generator no se distribuye hoy como una aplicación independiente que instalás con un npm install -g code-testing-generator.
Forma parte del tooling de testing de Microsoft y está disponible como agente/skill dentro del ecosistema dotnet-test. La implementación y las referencias pueden inspeccionarse en los repositorios públicos de Microsoft. (GitHub)
Los requisitos documentados incluyen:
VS Code
GitHub Copilot
un proyecto con build/test configurado
un framework de testing instalado o instalable
(GitHub)
Una vez disponible el agente en tu entorno, el workflow puede arrancar con algo tan sencillo como pedir:
Generate unit tests for this project.
Pero hay una forma mejor de usarlo.
Por ejemplo:
Generate Jest unit tests for the authentication service.
Focus on code that doesn't currently have test coverage.
O:
Generate unit tests for the payment calculation module.
Do not modify production code.
Especificar framework o scope reduce decisiones innecesarias y ayuda al agente a concentrarse en la parte del codebase que realmente querés cubrir.
Paso 1: discovery
La primera fase no debería producir código.
El researcher inspecciona el proyecto y trata de entender cómo funciona.
En un repo real podría descubrir algo parecido a:
src/
services/
invoice.ts
pricing.ts
customer.ts
tests/
invoice.test.ts
customer.test.ts
Hay tests para invoice y customer.
pricing.ts no tiene ninguno.
Pero eso todavía no significa automáticamente:
escribí pricing.test.ts.
Primero necesita leer los tests existentes y aprender las convenciones del proyecto.
Quizás encuentre:
describe("InvoiceService", () => {
...
})
Mocks con una librería determinada.
Factories para crear fixtures.
Helpers compartidos.
Una estructura concreta para nombres.
El objetivo es que el agente no invente un mini-framework de testing nuevo dentro de tu repo.
Paso 2: planificación
Después del research aparece un artifact especialmente útil:
.testagent/plan.md
La documentación de Microsoft hace referencia explícita a este plan durante troubleshooting. (GitHub)
Ese archivo importa porque crea un punto de revisión antes de que el agente genere una montaña de código.
Podés inspeccionar:
qué archivos piensa testear
qué escenarios encontró
qué mocks necesita
qué edge cases identificó
qué estructura propone
Este patrón merece copiarse en otros agentes.
No:
prompt → 37 archivos modificados
Sino:
prompt
↓
research
↓
plan revisable
↓
implementación
El plan se convierte en una frontera entre razonamiento y acción.
Paso 3: implementación
El implementer toma una fase del plan y escribe los tests.
Microsoft diseñó el sistema como polyglot: la documentación del repo describe al researcher, planner, implementer, builder, tester y linter como agentes capaces de trabajar con distintos lenguajes. También existen extensiones específicas para ayudar a detectar frameworks y convenciones según el ecosistema. (GitHub)
Por ejemplo, para .NET puede detectar frameworks como:
MSTest
xUnit
NUnit
TUnit
La misma filosofía se aplica a otros stacks: entender primero qué usa el repo en lugar de imponer una herramienta.
Paso 4: build
Acá empieza la parte que separa generación de código de engineering verificable.
Después de escribir los tests, el agente compila.
Si falla:
generated tests
↓
build
↓
FAILURE
↓
code-testing-fixer
↓
build
Microsoft incluso recomienda que durante implementación se compile solamente el proyecto de tests correspondiente para acelerar el loop, y hacer un build completo del workspace al final. (GitHub)
Es un detalle pequeño, pero demuestra que el workflow fue pensado para repos reales, donde recompilar una solución entera después de cada cambio puede ser carísimo.
Paso 5: ejecutar los tests
Una vez que compila, llega la prueba real.
El tester ejecuta el suite.
Si algo falla, hay una regla particularmente buena en la documentación:
no asumir que el código de producción está mal.
Microsoft advierte que muchos fallos en tests generados vienen de expected values incorrectos en las assertions.
El workflow recomendado es:
leer output real
↓
leer producción
↓
entender comportamiento correcto
↓
corregir assertion
No:
test falla
↓
cambiar producción hasta que pase
(GitHub)
Parece obvio cuando lo escribimos así.
Para un agente autónomo, no lo es.
Y nunca esconder el problema con Skip
La documentación añade otra regla que debería estar en cualquier agente de testing:
no marcar tests con Ignore o Skip simplemente para conseguir un suite verde. (GitHub)
Ese tipo de guardrail es precisamente lo que vuelve interesante un agente especializado.
Un agente general recibe:
arreglá los tests.
Y tiene cientos de maneras de producir aparentemente el resultado solicitado.
Un agente especializado puede tener reglas mucho más estrictas sobre qué constituye una solución válida.
Caso de uso 1: el módulo nuevo que salió sin tests
Imaginemos que un equipo acaba de agregar:
src/billing/discount-engine.ts
Tiene varios branches:
cliente nuevo
cliente premium
cupón expirado
descuento máximo
combinación de promociones
La feature funciona, pero el sprint terminó y nadie escribió el suite completo.
Este es prácticamente el caso ideal.
Le das scope al agente:
Generate unit tests for discount-engine.ts.
Use the existing test conventions.
Do not modify production code.
El agente puede estudiar los tests vecinos, identificar branches, planificar escenarios, generar casos, ejecutar el suite y corregir sus propios errores.
El humano revisa el resultado final.
No necesitó delegar la comprensión completa del sistema.
Delegó una tarea estrecha con un criterio de aceptación claro.
Caso de uso 2: un codebase viejo con cobertura desigual
El segundo escenario es probablemente más valioso.
Tenés un repo de cinco años.
Algunas áreas tienen tests excelentes.
Otras casi ninguno.
Pedirle a un agente:
aumentá la cobertura del proyecto
es peligrosamente abierto.
Pero podés darle un módulo concreto y dejar que primero haga discovery.
Por ejemplo:
Analyze src/payments for missing unit-test coverage.
Create a test plan first.
Then generate and validate tests using the project's existing framework and conventions.
Ahí el agente funciona más como test engineer asistido que como generador de snippets.
Y el plan previo te permite detenerlo antes de que toque código si entendió mal la arquitectura.
Caso de uso 3: el PR generado por otro agente
Hay un escenario todavía más interesante.
Claude Code implementa una feature.
Después, en vez de pedirle al mismo agente:
ahora escribí tus propios tests,
podrías enviar el diff a un agente especializado.
coding agent
↓
implementation
↓
testing agent
↓
tests
↓
build + execution
↓
human review
Eso introduce cierta separación de responsabilidades.
El agente que produjo la implementación no es necesariamente quien decide cómo demostrar que funciona.
No es independencia perfecta —ambos pueden usar modelos similares—, pero arquitectónicamente es mucho más sano que un único agente siendo simultáneamente autor, tester y juez de su propio trabajo.
La lección más grande no es sobre unit testing
code-testing-generator es interesante por lo que hace.
Pero me parece todavía más interesante por cómo está diseñado.
Estamos construyendo muchísimos agentes así:
Agente de Engineering
Puede:
- escribir código
- hacer reviews
- desplegar
- investigar bugs
- modificar infraestructura
- consultar producción
- actualizar Jira
- escribir documentación
- abrir PRs
- ejecutar comandos
Eso suena impresionante en una demo.
También crea un sistema con enorme superficie de permisos y una definición bastante vaga de éxito.
El patrón de Microsoft va en la dirección contraria:
responsabilidad estrecha
+
tools específicas
+
workflow estructurado
+
feedback automático
+
criterio verificable
Ese diseño tiene una propiedad que probablemente va a importar muchísimo cuando empecemos a poner agentes en producción:
podemos saber cuándo terminó bien.
Los mejores agentes quizás sean aburridos
Hay una tentación comprensible de imaginar el futuro como un swarm de agentes autónomos administrando toda la organización.
Puede que lleguemos ahí.
Pero buena parte del valor inmediato probablemente venga de algo bastante menos cinematográfico:
agente que escribe tests
agente que actualiza dependencias
agente que clasifica bugs
agente que revisa migrations
agente que reproduce errores
agente que actualiza documentación
Cada uno con acceso solamente a las herramientas que necesita.
Cada uno con un output definido.
Y, cuando sea posible, cada uno con una forma automática de demostrar que hizo correctamente su trabajo.
code-testing-generator es un buen ejemplo porque testing tiene ese loop incorporado de fábrica:
generar → ejecutar → observar → corregir → verificar.
No elimina el code review. Tampoco demuestra que los tests generados sean necesariamente los tests que un senior engineer habría escrito.
Lo que hace es mucho más concreto: automatiza una tarea costosa y repetible sin pretender que el agente sea capaz de hacer absolutamente todo.
Y quizás ésa sea una arquitectura mucho más interesante para la próxima generación de agentes.
Fuentes
