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 renomeada Classificação Porta de entrada Símbolo interno, helper privado, arquivo local Estrutural Suíte existente passa sem edições Função exportada, campo API/GraphQL, rota, flag CLI, coluna DB, nome de evento, chave de configuração Comportamental — quebra consumidores Test-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
| Desculpa | Realidade |
|---|---|
| "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
sleepreal,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.