Desarrollo Guiado por Pruebas
Filosofía
Principio fundamental: Las pruebas deben verificar el comportamiento a través de interfaces públicas, no los detalles de implementación. El código puede cambiar por completo; las pruebas no deberían.
Las pruebas buenas son de estilo integración: ejercitan rutas de código reales a través de APIs públicas. Describen qué hace el sistema, no cómo lo hace. Una buena prueba se lee como una especificación — "el usuario puede finalizar la compra con un carrito válido" te dice exactamente qué capacidad existe. Estas pruebas sobreviven a refactorizaciones porque no les importa la estructura interna.
Las pruebas malas están acopladas a la implementación. Simulan colaboradores internos, prueban métodos privados o verifican a través de medios externos (como consultar una base de datos directamente en lugar de usar la interfaz). La señal de advertencia: tu prueba se rompe cuando refactorizas, pero el comportamiento no ha cambiado. Si renombras una función interna y las pruebas fallan, esas pruebas estaban probando implementación, no comportamiento.
Consulta tests.md para ejemplos y mocking.md para pautas sobre mocking.
Anti-Patrón: Capas Horizontales
NO escribas todas las pruebas primero, luego toda la implementación. Esto es "corte horizontal" — tratar ROJO como "escribe todas las pruebas" y VERDE como "escribe todo el código."
Esto produce pruebas pésimas:
- Las pruebas escritas en masa prueban un comportamiento imaginado, no un comportamiento real
- Terminas probando la forma de las cosas (estructuras de datos, firmas de funciones) en lugar del comportamiento orientado al usuario
- Las pruebas se vuelven insensibles a cambios reales — pasan cuando el comportamiento se rompe, fallan cuando el comportamiento está bien
- Te adelantas a lo que puedes manejar, comprometiéndote con una estructura de prueba antes de entender la implementación
Enfoque correcto: Capas verticales mediante balas trazadoras. Una prueba → una implementación → repetir. Cada prueba responde a lo que aprendiste del ciclo anterior. Como acabas de escribir el código, sabes exactamente qué comportamiento importa y cómo verificarlo.
INCORRECTO (horizontal):
ROJO: test1, test2, test3, test4, test5
VERDE: impl1, impl2, impl3, impl4, impl5
CORRECTO (vertical):
ROJO→VERDE: test1→impl1
ROJO→VERDE: test2→impl2
ROJO→VERDE: test3→impl3
...
Flujo de Trabajo
1. Planificación
Al explorar el código fuente, lee CONTEXT.md (si existe) para que los nombres de las pruebas y el vocabulario de la interfaz coincidan con el lenguaje del dominio del proyecto, y respeta las ADR en el área que estás tocando.
Antes de escribir cualquier código:
- Confirmar con el usuario qué cambios de interfaz se necesitan
- Confirmar con el usuario qué comportamientos probar (priorizar)
- Identificar oportunidades para módulos profundos (interfaz pequeña, implementación profunda) — ejecuta la skill
/codebase-designpara el vocabulario y las comprobaciones de testabilidad - Listar los comportamientos a probar (no los pasos de implementación)
- Obtener la aprobación del usuario sobre el plan
Pregunta: "¿Cómo debería ser la interfaz pública? ¿Qué comportamientos son más importantes de probar?"
No puedes probarlo todo. Confirma con el usuario exactamente qué comportamientos importan más. Enfoca el esfuerzo de prueba en rutas críticas y lógica compleja, no en cada posible caso límite.
2. Bala Trazadora
Escribe UNA prueba que confirme UNA cosa sobre el sistema:
ROJO: Escribir prueba para el primer comportamiento → la prueba falla
VERDE: Escribir código mínimo para que pase → la prueba pasa
Esta es tu bala trazadora — demuestra que la ruta funciona de extremo a extremo.
3. Bucle Incremental
Para cada comportamiento restante:
ROJO: Escribir la siguiente prueba → falla
VERDE: Código mínimo para que pase → pasa
Reglas:
- Una prueba a la vez
- Solo el código suficiente para que pase la prueba actual
- No anticipar pruebas futuras
- Mantener las pruebas centradas en el comportamiento observable
4. Refactorizar
Después de que todas las pruebas pasen, busca candidatos a refactorización:
- Extraer duplicación
- Profundizar módulos (mover complejidad detrás de interfaces simples)
- Aplicar principios SOLID donde sea natural
- Considerar qué revela el nuevo código sobre el código existente
- Ejecutar pruebas después de cada paso de refactorización
Nunca refactorices mientras estés en ROJO. Llega a VERDE primero.
Lista de Verificación por Ciclo
[ ] La prueba describe comportamiento, no implementación
[ ] La prueba usa solo la interfaz pública
[ ] La prueba sobreviviría a una refactorización interna
[ ] El código es mínimo para esta prueba
[ ] No se añadieron características especulativas