Communitygithub.com

lguidolin/tests-as-a-control

Úsalo al escribir o modificar pruebas, cuando una prueba falla durante una refactorización o al probar reglas de permisos/roles. Síntomas: tentación de editar una prueba para que pase, probar solo el camino feliz, una prueba de denegación que empezó a pasar, pruebas inestables, o incertidumbre sobre qué afirmar.

¿Qué es tests-as-a-control?

Este skill establece el principio fundamental de que una prueba es un **espécimen de control**: mantiene fija una expectativa para que cualquier cambio de comportamiento se vuelva *visible*. La disciplina central es 'no arregles la prueba para que coincida con el código; entiende por qué están en desacuerdo'. Define reglas estrictas para la refactorización (no se deben editar pruebas), cambios de requisitos (las pruebas deben actualizarse deliberadamente como cambios independientes revisables), y cambios estructurales (la suite completa debe pasar sin editar pruebas). Enfatiza la cobertura simétrica: cada regla debe probarse tanto para el actor autorizado como para el no autorizado (casos de permitir y denegar). También aborda pruebas inestables (aislamiento inmediato), nomenclatura que cruza límites de contrato (requiere ciclo test-first con agregar y deprecar), y recomienda pruebas DAMP (Descriptivas y con Frases Significativas) en lugar de DRY (No Te Repitas) para la claridad de las pruebas. Es compatible con cualquier plataforma de agente de IA o flujo de trabajo de desarrollo, y complementa el skill `superpowers:test-driven-development`.

Compatible con~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/lguidolin/agent-skills/tree/main/skills/tests-as-a-control

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

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 renombradoClasificaciónPuerta de entrada
    Símbolo interno, helper privado, archivo localEstructuralLa suite existente pasa sin editar
    Función exportada, campo API/GraphQL, ruta, flag de CLI, columna de BD, nombre de evento, clave de configuraciónDe comportamiento — rompe consumidoresTest-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

ExcusaRealidad
"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 sleep real, 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.

Individual skills in this repo

This repo contains 18 individual skills — each has its own dedicated page.

lguidolin/change-hygiene-and-code-craft

Use when writing or refactoring code, structuring a commit or PR, or deciding whether to abstract duplication. Symptoms — mixing reorg with logic changes, a PR doing several things at once, a file growing large, the second copy of similar code, or unsure whether to DRY something up.

lguidolin/cloud-delivery-aks

Use when deploying to Kubernetes or Azure Kubernetes Service (AKS), configuring cloud secrets, setting up progressive rollout/canary, per-PR ephemeral environments, or k8s health probes. Keywords — Kubernetes, AKS, Key Vault, Argo Rollouts, Flagger, canary, blue-green, liveness, readiness, PodDisruptionBudget, HPA, rollback, GHCR.

lguidolin/commit-history-rewrite

Use when an existing repository has messy commit history that needs to conform to conventional commits before adopting release-please, or when intermediate WIP/fixup/merge commits need to be cleaned up.

lguidolin/conventional-commits-and-releases

Use when committing, writing a commit message, opening a PR that will be squash-merged, or configuring automated versioning/changelogs. Keywords — conventional commits, release-please, semver, feat/fix/chore, breaking change, changelog.

lguidolin/defense-in-depth-security

Use when handling untrusted input, secrets, authentication/authorization, or dependencies — or threat-modeling a new surface. Keywords — STRIDE, threat model, least privilege, secrets management, supply chain, dependency scanning, input validation, audit log, defense in depth.

lguidolin/designing-before-building

Use when starting a feature, fixing a non-trivial bug, or about to write implementation code — before any code exists. Symptoms you need this: "this is simple, I'll just code it", reaching for the editor before a design is approved, or an idea that hasn't been turned into a spec and plan.

lguidolin/engineering-constitution

Use when starting work in a project that follows the engineering constitution, orienting to its rules, or deciding which engineering practice applies to a task — spec writing, commits, testing, security, deploys, database, or UI work.

lguidolin/graphql-contract-testing

Use when writing a GraphQL query/mutation that the UI and a test will share, or building route/schema contract or smoke tests. Symptoms — copying a query into a test, a test asserting on query text, schema change that didn't break the UI build, or RLS/permission drift. Keywords — graphql-codegen, typed document, contract test, route smoke test.

lguidolin/init-repo-CI

Use when setting up a new repository with conventional commits, release-please, and CI automation, or when retrofitting an existing repository that lacks automated versioning and PR validation workflows.

lguidolin/interface-craft-and-accessibility

Use when building or styling UI — components, layouts, forms, design tokens — or making accessibility decisions. Keywords — a11y, WCAG, keyboard navigation, focus state, contrast, design system, minimalist UI, component reuse, ARIA, semantic HTML.

lguidolin/merge-gates-and-automation

Use when setting up or changing CI, pre-push hooks, or a task runner, or deciding what must pass before merge. Symptoms — tempted to put authoritative checks only in a local hook, skip CI, bypass with --no-verify, or unsure what gates a merge vs. runs locally.

lguidolin/observability-and-slos

Use when adding logging, metrics, tracing, health checks, SLOs, or alerting — or when building a service surface that needs to be operable and debuggable. Keywords — structured logs, OpenTelemetry, correlation id, RED metrics, liveness, readiness, SLI, SLO, error budget, alerting.

lguidolin/performance-and-scale

Use when working on hot paths, list endpoints, pagination, data-access in loops, or public interfaces/schemas. Symptoms — unbounded queries, N+1 access, no latency budget, optimizing without measuring, or changing an interface many consumers depend on. Keywords — pagination, N+1, Hyrum's Law, performance budget, bundle size.

lguidolin/postgres-postgraphile-rls-and-sql

Use when writing PostgreSQL, PostGraphile config, Row-Level Security policies, SQL schema files, or working on the Browser→App→PostGraphile→Postgres data path. Keywords — RLS, SECURITY DEFINER, search_path, pgSettings, grants, roles, GraphQL depth limit, query cost, statement_timeout, SQL file organization.

lguidolin/recording-decisions

Use when a design or architecture decision has been made and needs to be captured — writing a decision record or ADR, updating a decision index, noting a deferred idea, or superseding a past decision. Keywords — ADR, decision record, rationale, rejected alternatives, dependency index.

lguidolin/resilience-and-deploy-safety

Use when planning a deploy, designing a rollback, or responding to an incident or writing a postmortem. Keywords — deploy safety, rollback, immutable artifact, progressive delivery, canary, blast radius, incident response, blameless postmortem, error budget.

lguidolin/ship-it

Use when the user wants to ship work — push, PR, archive decision records, merge, and clean up. Handles the full lifecycle from committing final changes through post-merge cleanup including converting specs/plans to compact decision records.

lguidolin/zero-downtime-migrations

Use when changing a database schema where data must survive the change — adding/removing/renaming columns, constraints, indexes, or backfilling. Symptoms — a destructive migration bundled with a code deploy, a NOT NULL column with a backfill, a table-locking UPDATE, or a rename. Keywords — expand/contract, parallel change, backfill, NOT VALID, CREATE INDEX CONCURRENTLY, graphile-migrate.

Skills relacionados