🔄 retry
retry(attempts: number, cb: () => void)|retry(config: RetryConfig, cb: () => void)
retry é um auxiliar projetado para detectar testes instáveis (flaky tests) reexecutando testes que falharam um número especificado de vezes.
History
| Version | Changes |
|---|---|
| v4.4.0 | retry. |
retry é uma ferramenta de diagnóstico, não uma solução.
Use retry para identificar testes instáveis que precisam ser corrigidos. Um teste que requer retentativas é um teste que deve ser investigado e corrigido. Depender de retentativas para aprovar testes mascara problemas subjacentes e leva a suítes de teste não confiáveis.
Uso Básico
Retentativa simples
Retentar um teste até 3 vezes:
import { retry, it, assert } from 'poku';
await retry(3, () => {
it('teste instável', () => {
// Este teste pode falhar ocasionalmente
assert.strictEqual(Math.random() > 0.5, true);
});
});
Com objeto de configuração
Especifique tentativas e atraso entre retentativas:
import { retry, it, assert } from 'poku';
await retry({ attempts: 3, delay: 1000 }, () => {
it('teste instável', () => {
// Retentar até 3 vezes com 1 segundo de atraso entre tentativas
assert.strictEqual(Math.random() > 0.5, true);
});
});
Retentativas aninhadas
Você pode aninhar blocos retry para cenários complexos:
import { retry, it, assert } from 'poku';
await retry(2, async () => {
await retry(3, () => {
it('teste instável aninhado', () => {
// Externo: 2 tentativas, Interno: 3 tentativas por tentativa externa
assert.strictEqual(Math.random() > 0.5, true);
});
});
});
Retentativa ao redor de describe
Envolva blocos describe inteiros:
import { retry, describe, it, assert } from 'poku';
await retry(2, () => {
describe('suíte instável', () => {
it('teste 1', () => {
assert.strictEqual(1, 1);
});
it('teste 2', () => {
// Este teste pode falhar ocasionalmente
assert.strictEqual(Math.random() > 0.5, true);
});
});
});
Configuração
RetryConfig
type RetryConfig = {
attempts: number; // Número máximo de tentativas (incluindo a primeira execução)
delay?: number; // Atraso em milissegundos entre retentativas (padrão: 0)
};
Quando Usar retry
✅ Casos de uso apropriados
- Identificar testes instáveis: Adicione
retrytemporariamente para confirmar que um teste é instável - Investigar falhas: Use
retrydurante a depuração para coletar mais informações - Dependências externas: Testes que dependem de serviços externos com instabilidade conhecida (use com moderação)
❌ Casos de uso inapropriados
- Esconder testes quebrados: Não use
retrypara fazer testes com falha passarem - Solução permanente: Se um teste precisa de retentativas, ele precisa ser corrigido
- Mascarar condições de corrida: Corrija o problema de timing subjacente em vez disso
Boas Práticas
- Use temporariamente: Adicione
retrypara identificar instabilidade, depois remova após corrigir a causa raiz - Investigue falhas: Quando um teste passa com
retrymas falha sem ele, investigue o porquê - Corrija o teste: Resolva o problema subjacente (timing, estado, dependências externas)
- Remova o
retry: Uma vez corrigido, remova o wrapperretrypara garantir que o teste é estável
Uma suíte de testes saudável deve ter zero uso de retry. Se você se pegar adicionando retry frequentemente, é um sinal de que seus testes precisam de melhorias arquiteturais.
Como Funciona
retry usa um contexto baseado em pilha (stack) para suportar retentativas aninhadas:
- Quando um teste falha dentro de um bloco
retry, ele marca o contexto atual como falho - A função
retryreexecuta o bloco inteiro atéattemptsvezes - Blocos
retryaninhados funcionam independentemente, cada um com seu próprio contador de tentativas - Blocos
describepropagam seu status de falha para o contextoretrymais externo
Eficiência de Memória
retry usa alocação preguiçosa (lazy allocation): a pilha só é criada quando retry é chamado pela primeira vez e resetada para null quando vazia. Se você nunca usar retry, não há overhead nenhum.
Exemplos
Detectando uma condição de corrida
import { retry, it, assert } from 'poku';
// Este teste falha intermitentemente devido a uma condição de corrida
await retry(5, async () => {
await it('operação assíncrona completa', async () => {
const result = await algumaOperacaoAssincrona();
assert.strictEqual(result, 'esperado');
});
});
// Após identificar a instabilidade, corrija a condição de corrida:
// - Adicione sincronização adequada
// - Use timing determinístico
// - Mock dependências externas
// Depois remova o wrapper retry
Testando API externa com instabilidade conhecida
import { retry, it, assert } from 'poku';
// API externa ocasionalmente retorna 503
await retry({ attempts: 3, delay: 2000 }, async () => {
await it('API externa responde', async () => {
const response = await fetch('https://api-instavel.exemplo.com/dados');
assert.strictEqual(response.status, 200);
});
});
// Abordagem melhor: mock a API externa nos testes
// Use retry apenas durante o desenvolvimento para identificar a instabilidade
Relacionados
describe: Agrupar testesit: Definir testes individuaisskip: Pular testesbeforeEach/afterEach: Setup e teardown