Communitygithub.com

Testes como Controle

Use ao escrever ou modificar testes, quando um teste quebra durante uma refatoração, ou ao testar regras de permissão/papéis. Sintomas — tentação de editar um teste para fazê-lo passar, testar apenas o caminho feliz, um teste de negação que começou a passar, testes instáveis, ou incerteza sobre o que afirmar.

O que é Testes como Controle?

Este skill estabelece que testes são espécimes de controle: eles fixam uma expectativa para que qualquer mudança de comportamento se torne visível. A disciplina central é não consertar o teste para corresponder ao código, mas entender por que eles discordam. O skill cobre regras rigorosas para mudanças comportamentais (test-first), mudanças estruturais (a suíte existente deve passar sem edições nos testes) e renomeações públicas (tratadas como mudanças comportamentais). Também define cobertura simétrica (testar regras em ambas as direções — permitir e negar), classificação de testes por tamanho (pequeno, médio, grande), e quarentena imediata de testes instáveis. O conteúdo é útil para desenvolvedores que usam agentes de IA como Claude Code, Cursor ou Codex, e está documentado em formato SKILL.md com regras claras, tabelas de racionalizações comuns e bandeiras vermelhas para parar. A implementação segue a Seção IV da constituição de engenharia, disponível no repositório `engineering-constitution/references/engineering-constitution.md`.

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

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

O que Testes como Controle faz?

Visão Geral

Um teste é um espécime de controle: ele mantém uma expectativa fixa para que qualquer mudança de comportamento se torne visível. A disciplina é não consertar o teste para corresponder ao código; entenda por que eles discordam.

As Regras

  • Test-first para mudança comportamental. Escreva o teste que falha, veja-o falhar pela razão certa, depois escreva o código mínimo que passa. Se você não viu o teste falhar, você não sabe que ele testa o que você pensa. O ciclo em si é superpowers:test-driven-development; este skill rege o que o teste deve conter e o que uma falha significa uma vez que ele existe. A regra vale independentemente de esse skill estar instalado.

  • Mudança estrutural é verificada pela suíte inalterada. Uma mudança puramente estrutural — renomear, mover, dividir, extrair, sem mudança de comportamento observável — não requer novo teste. Sua porta de entrada é o oposto: toda a suíte existente passa com nenhum teste editado. É por isso que test-first não se aplica aqui — não há comportamento a especificar, então nenhum teste pode falhar de forma significativa primeiro. Se uma mudança estrutural força uma edição de teste, não era estrutural. Pare e reclassifique usando a próxima regra.

  • Uma renomeação que cruza uma fronteira de contrato não é uma refatoração. A questão não é "o código mudou de comportamento?", mas "este nome é observável para algo que eu não controlo?"

    Coisa renomeadaClassificaçãoPorta de entrada
    Símbolo interno, helper privado, arquivo localEstruturalSuíte existente passa sem edições
    Função exportada, campo API/GraphQL, rota, flag CLI, coluna DB, nome de evento, chave de configuraçãoComportamental — quebra consumidoresTest-first + adicionar e depreciar

    Uma renomeação pública faz a mesma coisa com os mesmos dados e ainda é uma mudança que quebra, porque o nome era o contrato (Lei de Hyrum — veja performance-and-scale). Trate como comportamental: adicione o novo nome com um ciclo de test-first com falha, mantenha o nome antigo e seu teste de aprovação vivo durante a janela de depreciação, remova o antigo somente depois que os consumidores migraram.

    O terceiro caso é um teste que referencia um nome como uma string — snapshots, chaves de contêiner DI, reflexão, caminhos de fixtures. Eles quebram em uma renomeação interna. Isso não é a regra falhando; é o sinal de que o teste estava acoplado a um detalhe de implementação. Corrija o acoplamento, não redefina a linha de base.

  • Refatorações não devem editar testes; mudanças de requisitos, sim. Atos diferentes:

    • Uma refatoração quebra um teste → o teste pegou uma mudança de comportamento não intencional. Pare e entenda. Editar o teste para fazer uma refatoração passar destrói sua instrumentação.
    • Um requisito genuinamente mudou → atualize o teste deliberadamente, como sua própria alteração revisável, registrada como tal.
    • O movimento proibido é redefinir silenciosamente a linha de base.
  • Cobertura simétrica — toda regra testada em ambas as direções. Prove que o ator autorizado pode e que o ator não autorizado não pode. Testes apenas do caminho feliz testam metade de uma regra.

  • Regras de permissão/papéis são uma matriz: toda célula significativa (papel × ação × recurso), permitir e negar. Um novo papel está incompleto até que seus casos de negação existam. Um teste de negação que de repente passa é uma regressão de segurança — revise e aceite explicitamente, nunca redefina linha de base.

  • Pontos de entrada nunca são adicionados/removidos silenciosamente. Rotas, comandos, endpoints cada um recebe cobertura de existência-e-fumaça para que adicionar/remover force uma mudança de teste.

  • Teste por tamanho: pequeno (lógica pura, sem I/O, constante), médio (DB/processo real, hermético), grande (stack completo). Muitos pequenos, poucos médios, menos grandes. Observe o meio ausente — lógica de servidor/aplicativo que nem unitário nem integração cobre porque cada camada assume que a outra o testa.

  • Testes instáveis são colocados em quarentena na hora. Um teste que passa/falha sem uma mudança de código destrói a confiança em toda a suíte. Coloque em quarentena imediatamente (fora da porta de bloqueio, abra um conserto) — nunca deixe falhando intermitentemente, nunca delete silenciosamente. Testes pequenos devem ser determinísticos: sem relógio real, aleatoriedade ou rede.

  • Código de teste é DAMP, não DRY. Frases Descritivas e Significativas em vez de desduplicação. Um teste deve ser óbvio lido isoladamente. Aplicar DRY a testes é uma violação, não uma virtude (veja change-hygiene-and-code-craft).

Racionalizações Comuns

DesculpaRealidade
"Vou escrever o código primeiro, depois adicionar testes"Então o teste foi ajustado ao código, não ao requisito. Você nunca o viu falhar, então não sabe se ele testa algo.
"É só uma renomeação, não precisa de teste"Correto — se o nome for interno. Se for exportado/público, o nome era o contrato e a renomeação é comportamental.
"Renomeei e tive que consertar três testes, é normal"Não é. Ou você cruzou uma fronteira de contrato, ou esses testes estavam acoplados a detalhes de implementação. Reclassifique.
"O teste está errado agora, vou só atualizá-lo"Somente se um requisito mudou. Se você está refatorando, o teste pegou uma mudança real — investigue.
"Testei o caminho feliz, é suficiente"Uma regra só é testada quando o caso de negação também está.
"Este teste de negação passou agora, ótimo, pode enviar"Uma ação proibida bem-sucedida é uma regressão de segurança, não progresso.
"O teste está instável, só re-executar o CI"Instabilidade mascara bugs reais e treina todos a ignorar falhas. Coloque em quarentena e corrija.
"Deixe-me aplicar DRY nesses testes com um helper compartilhado"Testes são DAMP. Duplicação que ajuda na clareza local permanece.

Bandeiras Vermelhas — PARE

  • Escrever código de produção para uma mudança de comportamento antes de existir um teste com falha
  • Uma mudança estrutural cujo diff também toca arquivos de teste
  • Renomear um nome público/exportado no lugar em vez de adicionar e depreciar
  • Editar um teste durante uma refatoração para fazê-lo passar
  • Uma nova permissão sem teste de caso de negação
  • Redefinir linha de base de um snapshot/assertiva sem entender por que mudou
  • Um teste com sleep real, Date.now(), dados aleatórios ou chamada de rede
  • Refatorar código de teste para remover duplicação ao custo da legibilidade

Raciocínio completo: Artigo IV da constituição, empacotado em engineering-constitution/references/engineering-constitution.md. Para os detalhes específicos de contrato GraphQL/rota, veja 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.

Habilidades Relacionadas