Test Smells.
Recurring problems in test code — how to recognize each one, why it hurts, how to fix it, and the lint rules that detect it.
Um teste empacota muitas asserções não documentadas em um único método, então, quando ele falha, você não consegue saber qual asserção disparou nem por quê — é preciso apostar.
Um Teste Ávido verifica vários métodos ou comportamentos distintos da unidade sob teste em um único método de teste, em vez de focar em um único comportamento.
Uma asserção redundante compara um valor consigo mesmo ou com um literal que é igual por construção, de modo que seu resultado é fixo e ela nunca pode realmente falhar ou detectar uma regressão.
Um método de teste que exercita o código mas não contém nenhuma asserção, de modo que passa desde que nada lance uma exceção — deixando desconhecido seu real propósito e o que ele verifica.
Um único método de teste verifica a mesma condição mais de uma vez — repetindo uma asserção idêntica ou reverificando uma lógica equivalente — em vez de remover a verificação redundante ou separar casos distintos em seus próprios testes focados.
Um teste verifica o comportamento comparando a representação em string de um objeto (toString(), JSON.stringify(), HTML renderizado) com um literal de string esperado, acoplando o teste a detalhes incidentais de formatação em vez dos valores que realmente importam.
Uma configuração compartilhada constrói uma única fixture gigante, do tipo “tudo em um”, que cobre as necessidades de todos os testes, mas cada teste individual exercita apenas uma pequena fatia dela.
Um teste cujas entradas ou resultados esperados ficam em um recurso externo — um arquivo, um seed de banco de dados ou uma fixture compartilhada — de modo que você não consegue entender nem confiar no teste apenas lendo-o.
Otimismo de Recursos é quando um teste presume que um recurso externo (um arquivo, diretório, tabela de banco de dados, variável de ambiente ou endpoint de rede) já existe e está em um estado conhecido, em vez de provisioná-lo e verificá-lo, fazendo o teste passar ou falhar de forma não determinística.
Uma classe de teste inicializa seus campos de fixture em um construtor em vez de no hook de setup dedicado do framework (setUp / @BeforeEach / TestInitialize), contornando o ciclo de vida do teste.
Um Teste Obscuro é aquele em que o leitor não consegue dizer, apenas pelo método de teste, qual cenário é montado, qual comportamento é exercitado e qual resultado é esperado — porque a intenção está soterrada em detalhes demais, contexto de menos ou lógica escondida em outro lugar.
Um teste que usa `if`/`switch`/ternários, laços ou `try`/`catch` para decidir o que executar ou verificar, de modo que seu comportamento — e se ele verifica algo de fato — depende de qual ramo é executado em tempo de execução.
Duplicação de Código de Teste é quando o mesmo código de configuração, ação ou asserção é copiado e colado em muitos testes, de modo que uma única mudança força edições em muitos lugares e os testes apodrecem em cópias frágeis e quase idênticas.
Um teste fixa literais numéricos sem explicação em suas entradas e asserções, escondendo o que os números significam e de onde vieram.
Um teste que usa muito mais código do que precisa para declarar seu cenário, soterrando a única relação de causa e efeito que verifica sob boilerplate de configuração, dados irrelevantes e asserções campo a campo.
Um teste errático (instável) passa e falha de forma intermitente sobre o mesmo código porque seu resultado depende de tempo, ordenação, estado compartilhado ou outros fatores não determinísticos, em vez do comportamento sob teste.
Um Sleepy Test pausa a execução com um atraso fixo no código (`Thread.sleep`, `setTimeout`, `cy.wait(2000)`, `page.waitForTimeout`) para esperar por um trabalho assíncrono em vez de esperar pela condição real.
Uma Test Run War acontece quando os testes passam para uma pessoa, mas falham aleatoriamente assim que várias pessoas ou jobs de CI executam a suíte ao mesmo tempo, porque os testes colidem em uma fixture compartilhada e persistente.
Um teste passa ou falha dependendo de quais outros testes rodaram antes dele, porque os testes vazam e dependem de estado mutável compartilhado em vez de cada um montar e desmontar a sua própria fixture.
Um teste configura tantos objetos mock e interações stubadas que a configuração dos mocks ofusca a verificação propriamente dita, então o teste acaba exercitando os mocks em vez do comportamento real.
Um teste faz asserções sobre como o código funciona internamente — campos privados, chamadas a métodos internos, estrutura do DOM ou classes CSS — em vez do comportamento observável do qual um consumidor real depende.
Um teste superespecificado afirma muito mais do que o comportamento sob teste exige—fixando strings de saída exatas, formatos completos de objetos, ordem de coleções ou cada chamada de colaborador interno—de modo que ele quebra sempre que um detalhe de implementação não relacionado muda.
Um Teste Vazio é um método de teste cujo corpo não contém nenhuma instrução executável, de modo que ele sempre passa enquanto não verifica nada.
Um teste que está commitado na base de código mas nunca roda porque foi pulado, desabilitado ou comentado, dando a aparência de cobertura sem de fato verificar nada.
O código de produção contém lógica, ramos ou membros que existem apenas para dar suporte aos testes, borrando a linha entre o que vai para produção e o que é apenas testado.
O código de produção contém métodos, acessadores de estado ou seams (costuras) que existem apenas para serem usados pelos testes, poluindo a API real e convidando a testes que verificam detalhes internos em vez do comportamento.
Código de produção cujo design (acoplamento forte, dependências ocultas, estado global, IO não determinística ou interfaces apenas assíncronas) força os testes a contorções estranhas, ou torna impossível exercitar uma unidade isoladamente.