---
title: "Teste Errático (Instável)"
type: "test-smell"
slug: "erratic-test"
url: "http://localhost:3000/pt-br/test-smells/erratic-test.md"
category: "Maus Cheiros Erráticos"
description: "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."
---
# Teste Errático (Instável)

> 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.

## Signs and Symptoms

Um teste que está **verde em uma execução e vermelho na seguinte sem nenhuma alteração de código** é errático. Você o reconhece tanto pelo comportamento humano ao seu redor quanto pelo código: desenvolvedores reexecutam a CI "para fazer passar," adicionam `@Flaky`/`retry(3)` ou colocam o teste em quarentena em vez de corrigi-lo.

Sinais comuns no código e no padrão de falha:

* **Tempo/assíncrono:** "esperas" com `sleep`/`setTimeout`, promises não aguardadas ou asserções que correm contra o sistema sob teste. As falhas se correlacionam com a velocidade da máquina ou a carga da CI.
* **Dependência de ordem:** o teste passa isoladamente mas falha na suíte completa, ou vice-versa. Embaralhar a ordem dos testes muda o resultado.
* **Estado mutável compartilhado:** coleções estáticas, uma linha de banco de dados reaproveitada, um singleton ou uma fixture mutada por um teste anterior.
* **Entradas não determinísticas:** `Date.now()`/`new Date()`, `Math.random()`, locale/fuso horário, ordem de iteração de hash-map ou IDs gerados automaticamente.
* **Recursos externos:** rede, sistema de arquivos ou relógio reais. As falhas parecem timeouts ou "connection refused," não divergências de asserção.
* **Asserções condicionais:** `expect` escondido dentro de `if`/`catch`/callbacks, então o teste passa silenciosamente quando o ramo nunca é executado.

```js
// Smell: uma "espera" fixa, um array compartilhado e um relógio real
const created = [];                       // compartilhado entre testes → testes interagentes

test('shows a fresh receipt', async () => {
  render(<Checkout />);
  fireEvent.click(screen.getByText('Pay'));
  await new Promise(r => setTimeout(r, 300));   // torcer para que a requisição tenha terminado
  created.push('order-1');                       // vaza para testes posteriores
  expect(screen.getByRole('status'))
    .toHaveTextContent(`Paid ${new Date().toISOString()}`); // muda a cada execução
});

```

Isso mapeia diretamente para os sub-smells do _Teste Errático_ de Meszaros: **Testes Interagentes / Guerra de Execução de Testes** (estado compartilhado), **Teste Solitário** (só roda depois de outro), **Otimismo de Recursos** (assume que um recurso externo está disponível), **Vazamento de Recursos** (não faz limpeza) e **Teste Não Determinístico / Irrepetível** (tempo, aleatoriedade, assíncrono).

## Reasons for the Problem

**Por que isso acontece**

* **Tempo implícito.** Código assíncrono é "sincronizado" com `sleep`s fixos ou simplesmente sem aguardar. O atraso é um chute: suficiente hoje, curto demais sob carga amanhã.
* **Acoplamento oculto.** Os testes compartilham um banco de dados, um singleton, variáveis em nível de módulo ou arquivos em disco. Os efeitos colaterais de um teste tornam-se as precondições de outro (os _Testes Interagentes_ / _Guerra de Execução de Testes_ de Meszaros), então os resultados dependem da ordem e do que rodou em paralelo.
* **Entradas não determinísticas se infiltram.** O horário real do relógio, `Math.random()`, locale/fuso horário e coleções sem ordem variam entre execuções e máquinas.
* **Dependência otimista do ambiente.** Os testes acessam uma rede/serviço ao vivo ou assumem que um arquivo existe (_Otimismo de Recursos_) e nunca liberam o que adquirem (_Vazamento de Recursos_).
* **Asserções que podem ser puladas.** Colocar `expect` em um condicional ou em um `.catch` significa que a verificação pode nunca ser executada, então o teste "passa" por acidente.

**Por que isso é prejudicial**

* **Falsa confiança e defeitos mascarados.** Um teste instável pode falhar por motivos não relacionados a produção, _e_ uma regressão real pode se esconder atrás de uma falha que todos assumem ser "só instabilidade." Você não consegue mais distinguir sinal de ruído.
* **Erosão da confiança.** Quando uma suíte é conhecida por ser instável, os desenvolvedores ignoram builds vermelhos e reexecutam por reflexo, o que treina a equipe a desconsiderar a suíte de testes por completo.
* **Tempo desperdiçado e pipelines quebrados.** Retentativas, reexecuções e investigações do tipo "sou eu ou é o teste?" atrasam todo mundo e bloqueiam o CI/CD por não-problemas.
* **Baixa manutenibilidade.** Testes erráticos são difíceis de depurar porque a falha não é reproduzível; eles tendem a ser desativados em vez de corrigidos, reduzindo silenciosamente a cobertura real.

## Treatment

Trate a instabilidade como um defeito no teste, não como uma peculiaridade a ser contornada com retentativas. Reexecuções servem para **detectar** a instabilidade, nunca para **escondê-la**. Coloque o teste em quarentena se ele bloquear o pipeline, e então investigue a causa raiz.

**1\. Substitua sleeps por espera baseada em condição.** Faça polling do estado que você realmente quer em vez de chutar uma duração.

```js
// antes — atraso fixo com condição de corrida
fireEvent.click(screen.getByText('Pay'));
await new Promise(r => setTimeout(r, 300));
expect(screen.getByRole('status')).toBeInTheDocument();

// depois — aguardar a condição e então verificar
fireEvent.click(screen.getByText('Pay'));
expect(await screen.findByRole('status')).toBeInTheDocument();

```

**2\. Torne as entradas determinísticas.** Injete o relógio ou use fake timers; semeie ou faça stub da aleatoriedade; fixe locale/fuso horário.

```js
// antes: depende da data real
expect(label).toBe(`Paid ${new Date().toISOString()}`);

// depois: congelar o tempo (vitest/jest)
vi.useFakeTimers();
vi.setSystemTime(new Date('2026-06-15T00:00:00Z'));

```

**3\. Isole cada teste.** Dê a cada teste fixtures novas, redefina o estado compartilhado (banco de dados, singletons, globais de módulo) em `beforeEach`/`afterEach` e evite variáveis mutáveis em nível de módulo. Execute a suíte em **ordem aleatória** (por exemplo, `--seed`/randomize do Jest, `MethodOrderer.Random` do JUnit) para revelar dependências de ordenação cedo.

**4\. Faça stub de recursos externos.** Faça mock de rede, sistema de arquivos e chamadas de sistema para que o teste nunca dependa de um serviço estar no ar (cura o _Otimismo de Recursos_); sempre libere/limpe os recursos adquiridos (cura o _Vazamento de Recursos_).

**5\. Aguarde todo o trabalho assíncrono e verifique incondicionalmente.** Retorne/aguarde promises e utilitários de teste assíncronos; mova os efeitos colaterais para fora dos callbacks de `waitFor` para que rodem uma única vez; nunca enterre `expect` dentro de `if`/`catch`.

**6\. Verifique a correção.** Execute o teste agora determinístico muitas vezes (e sob carga / ordem embaralhada) para confirmar a estabilidade antes de tirá-lo da quarentena.

## Detected by

- **sonar** `java:S5973` — Os testes devem ser estáveis (https://rules.sonarsource.com/java/RSPEC-5973/)
- **sonar** `java:S2925` — "Thread.sleep" não deve ser usado em testes (https://rules.sonarsource.com/java/RSPEC-2925/)
- **eslint-jest** `no-conditional-expect` — no-conditional-expect (https://github.com/jest-community/eslint-plugin-jest/blob/main/docs/rules/no-conditional-expect.md)
- **eslint-vitest** `no-conditional-expect` — no-conditional-expect (https://github.com/veritem/eslint-plugin-vitest/blob/main/docs/rules/no-conditional-expect.md)
- **eslint-testing-library** `await-async-queries` — await-async-queries (https://github.com/testing-library/eslint-plugin-testing-library/blob/main/docs/rules/await-async-queries.md)
- **eslint-testing-library** `await-async-utils` — await-async-utils (https://github.com/testing-library/eslint-plugin-testing-library/blob/main/docs/rules/await-async-utils.md)
- **eslint-testing-library** `no-wait-for-side-effects` — no-wait-for-side-effects (https://github.com/testing-library/eslint-plugin-testing-library/blob/main/docs/rules/no-wait-for-side-effects.md)
