Pruebas como Control
Resumen
Una prueba es un espécimen de control: mantiene fija una expectativa para que cualquier cambio de comportamiento se vuelva visible. La disciplina es no arregles la prueba para que coincida con el código; entiende por qué están en desacuerdo.
Las Reglas
-
Test-first para cambios de comportamiento. Escribe la prueba fallida, obsérvala fallar por la razón correcta, luego escribe el código mínimo que pase. Si no la viste fallar, no sabes que prueba lo que crees. El bucle en sí mismo es
superpowers:test-driven-development; este skill gobierna lo que la prueba debe contener y qué significa una falla una vez que existe. La regla se mantiene ya sea que ese skill esté instalado o no. -
El cambio estructural se verifica con la suite inalterada. Un cambio puramente estructural — renombrar, mover, dividir, extraer, sin cambio de comportamiento observable — requiere ninguna prueba nueva. Su puerta de entrada es lo opuesto: toda la suite existente pasa sin que se haya editado ninguna prueba. Esta es la razón por la cual test-first no aplica aquí — no hay comportamiento que especificar, por lo que ninguna prueba puede fallar significativamente primero. Si un cambio estructural fuerza una edición de prueba, no fue estructural. Detente y reclasifica usando la siguiente regla.
-
Un renombre que cruza un límite de contrato no es una refactorización. La pregunta no es "¿cambió el comportamiento el código?" sino "¿es observable este nombre para algo que no controlo?"
Elemento renombrado Clasificación Puerta de entrada Símbolo interno, helper privado, archivo local Estructural La suite existente pasa sin editar Función exportada, campo API/GraphQL, ruta, flag de CLI, columna de BD, nombre de evento, clave de configuración De comportamiento — rompe consumidores Test-first + agregar y deprecar Un renombre público hace lo mismo con los mismos datos y sigue siendo un cambio disruptivo, porque el nombre era el contrato (Ley de Hyrum — ver
performance-and-scale). Trátalo como de comportamiento: agrega el nuevo nombre con un ciclo test-first de fallo, mantén el nombre antiguo y su prueba que pasa vivos durante el período de deprecación, elimina el antiguo solo cuando los consumidores hayan migrado.El tercer caso es una prueba que referencia un nombre como cadena — instantáneas, claves de contenedor de DI, reflexión, rutas de fixtures. Estas se rompen con un renombre interno. Eso no es que la regla falle; es la señal de que la prueba estaba acoplada a un detalle de implementación. Arregla el acoplamiento, no reajustes la línea base.
-
Las refactorizaciones no deben editar pruebas; los cambios de requisitos sí. Actos diferentes:
- Una refactorización rompe una prueba → la prueba detectó un cambio de comportamiento no intencionado. Detente y entiéndelo. Editar la prueba para que una refactorización pase destruye tu instrumentación.
- Un requisito cambió genuinamente → actualiza la prueba deliberadamente, como su propio cambio revisable, registrado como tal.
- El movimiento prohibido es el reajuste silencioso de la línea base.
-
Cobertura simétrica — cada regla probada en ambas direcciones. Demuestra que el actor autorizado puede y que el actor no autorizado no puede. Pruebas solo del camino feliz prueban la mitad de una regla.
-
Las reglas de permisos/roles son una matriz: cada celda (rol × acción × recurso) significativa, permitir y denegar. Un nuevo rol está incompleto hasta que existan sus casos de denegación. Una prueba de denegación que de repente pasa es una regresión de seguridad — revísala y acéptala explícitamente, nunca reajustes la línea base.
-
Los puntos de entrada nunca se agregan/eliminan en silencio. Rutas, comandos, endpoints reciben cobertura de existencia y humo, de modo que agregar/eliminar fuerza un cambio en la prueba.
-
Prueba por tamaño: pequeña (lógica pura, sin E/S, constante), mediana (BD/proceso real, hermética), grande (pila completa). Muchas pequeñas, menos medianas, pocas grandes. Observa el medio faltante — lógica de servidor/aplicación que ni las pruebas unitarias ni de integración cubren porque cada capa asume que la otra lo prueba.
-
Las pruebas inestables se ponen en cuarentena de inmediato. Una prueba que pasa/falla sin un cambio de código destruye la confianza en toda la suite. Ponla en cuarentena inmediatamente (fuera de la puerta de bloqueo, reporta una solución) — nunca dejes que falle intermitentemente, nunca la elimines en silencio. Las pruebas pequeñas deben ser deterministas: sin reloj real, aleatoriedad ni red.
-
El código de prueba es DAMP, no DRY. Descriptive And Meaningful Phrases (Frases Descriptivas y Significativas) en lugar de des-duplicación. Una prueba debe ser obvia al leerla de forma aislada. Aplicar DRY a las pruebas es una violación, no una virtud (ver
change-hygiene-and-code-craft).
Racionalizaciones Comunes
| Excusa | Realidad |
|---|---|
| "Escribiré el código primero, luego agregaré las pruebas" | Entonces la prueba fue ajustada al código, no al requisito. Nunca la viste fallar, por lo que no sabes que prueba algo. |
| "Es solo un renombre, no necesita prueba" | Correcto — si el nombre es interno. Si es exportado/público, el nombre era el contrato y el renombre es de comportamiento. |
| "Lo renombré y tuve que arreglar tres pruebas, eso es normal" | No lo es. O cruzaste un límite de contrato, o esas pruebas estaban acopladas a un detalle de implementación. Reclasifica. |
| "La prueba ahora está mal, solo la actualizaré" | Solo si cambió un requisito. Si estás refactorizando, la prueba detectó un cambio real — investiga. |
| "Probé el camino feliz, eso es suficiente" | Una regla no está probada hasta que también se pruebe el caso de denegación. |
| "Esta prueba de denegación pasa ahora, genial, envíalo" | Una acción prohibida que tiene éxito es una regresión de seguridad, no un progreso. |
| "La prueba es inestable, solo re-ejecuta CI" | La inestabilidad oculta errores reales y entrena a todos a ignorar fallas. Ponla en cuarentena y arréglala. |
| "Déjame aplicar DRY a estas pruebas con un helper compartido" | Las pruebas son DAMP. La duplicación que ayuda a la claridad en contexto se queda. |
Banderas Rojas — DETENTE
- Escribir código de producción para un cambio de comportamiento antes de que exista una prueba fallida
- Un cambio estructural cuyo diff también toca archivos de prueba
- Renombrar un nombre público/exportado en su lugar en lugar de agregar y deprecar
- Editar una prueba durante una refactorización para que se ponga verde
- Un nuevo permiso sin prueba de caso de denegación
- Reajustar la línea base de una instantánea/afirmación sin entender por qué cambió
- Una prueba con un
sleepreal,Date.now(), datos aleatorios o llamada de red - Refactorizar código de prueba para eliminar duplicación a costa de la legibilidad
Fundamento completo: Artículo IV de la constitución, incluido en engineering-constitution/references/engineering-constitution.md. Para los detalles específicos del contrato GraphQL/ruta, ver graphql-contract-testing.