Testes instáveis: o que os causa e como corrigi-los para sempre

  1. Início
  2. Blog
  3. Automação de testes

Automação de Teste

Testes instáveis: O que os causa e como corrigi-los para sempre

Um teste que falha aleatoriamente ensina sua equipe a ignorar as falhas. Aprenda como confirmar se um teste está instável, rastrear a causa real e corrigi-lo, com demonstrações mostrando o que realmente funciona.

9 min de leitura
Testes instáveis: o que os causa e como corrigi-los para sempre

Principais pontos

  • Um teste instável passa e falha no mesmo código, sem alterações.
  • As causas habituais são temporização, estado partilhado, dados instáveis, serviços externos e diferenças ambientais.
  • Substitua as pausas fixas por esperas por uma condição específica e forneça a cada teste seus próprios dados.
  • As novas tentativas podem ocultar bugs reais. Coloque um teste instável em quarentena, corrija-o dentro de um prazo ou exclua-o.

A compilação fica vermelha, alguém clica em “executar novamente”, ela é aprovada e todos seguem em frente. Parece inofensivo, mas testes instáveis destroem lentamente o que há de mais valioso em um conjunto de testes: a confiança das pessoas nele. Quando as falhas geralmente são barulhentas, bugs reais também são eliminados.

Este guia aborda por que os testes ficam instáveis ​​e como corrigir cada causa adequadamente. Executamos as demonstrações de tempo e estado compartilhado no Python 3.13, e os números mostrados vêm dessas execuções. São pequenas demonstrações projetadas para facilitar a visualização da instabilidade, de modo que suas próprias taxas de falha serão diferentes.

O que é um teste instável?

Um teste instável fornece resultados diferentes no mesmo código. Ele passa em uma execução e falha na próxima, mesmo que nada tenha mudado no produto. O teste pode estar errado ou o código pode conter um bug intermitente real. Essa segunda possibilidade é exatamente a razão pela qual você não deve simplesmente descartá-los.

Por que os testes instáveis são tão caros

  • Confiança perdida. Quando as pessoas presumem que vermelho significa “provavelmente esquisito”, elas param de investigar falhas reais.
  • Perda de tempo. Os desenvolvedores passam horas reexecutando pipelines e examinando falhas que não têm nada a ver com suas alterações.
  • Entrega mais lenta. Fusões e liberações aguardam repetições, minando o ponto de CI/CD.
  • Bugs ocultos. As condições de corrida no produto podem parecer ruídos de teste, e são essas que prejudicam a produção.

Resumo das causas comuns

Causa Sinal típico Correção usual
Tempo e assíncrono Falha mais quando o CI está ocupado Espere por uma condição
Estado compartilhado Falha dependendo da ordem do teste Isolar dados, redefinir estado
Datas e aleatoriedade Falha em determinados horários ou dias Controle o relógio e a semeadura
Seletores frágeis Falha após alterações na IU Use localizadores estáveis
Serviços externos Falha quando uma dependência é lenta Zombe ou esbofeteie-os
Diferenças ambientais Passa localmente, falha no CI Alinhar com contêineres
Limites de recursos Falha em execuções paralelas Ajuste os trabalhadores, corrija a contenção

Causa 1: tempo e sono fixo

Esta é a causa mais comum. Um teste pressupõe que o trabalho em segundo plano será concluído após um atraso fixo. Para ver o efeito, escrevemos um pequeno teste no qual o trabalho leva até 2 ms, enquanto o teste espera apenas 1 ms antes de verificar:

def flaky_timing():
    result = {}
    def work():
        time.sleep(random.uniform(0, 0.002))
        result["done"] = True
    threading.Thread(target=work).start()
    time.sleep(0.001)        # hope it has finished
    assert result.get("done")

A solução é aguardar a condição real, com um limite superior generoso:

def fixed_timing():
    result = threading.Event()
    def work():
        time.sleep(random.uniform(0, 0.002))
        result.set()
    threading.Thread(target=work).start()
    assert result.wait(timeout=1)   # continues as soon as it is ready

Executamos cada versão 200 vezes:

Versão Falhas em 200
Sono fixo 102
Espere por uma condição 0

O resultado é mais do que apenas confiabilidade. A versão fixa avança no momento em que o trabalho é concluído, por isso geralmente também é mais rápida. Um sono mais longo faria com que a primeira versão falhasse com menos frequência, mas cada execução seria mais lenta e ainda poderia falhar em uma máquina de CI ocupada.

Nos testes de navegador, o mesmo princípio se aplica. Use a espera automática de Playwright ou Esperas de Selenium explícitas, em vez de time.sleep:

# Playwright: retries until true or a timeout is reached
expect(page.get_by_text("Order placed")).to_be_visible()

# Selenium: an explicit wait for a condition
WebDriverWait(driver, 10).until(
    EC.visibility_of_element_located((By.ID, "confirmation"))
)

Causa 2: estado compartilhado e ordem de teste

Quando os testes compartilham dados, um pode afetar o outro, e o resultado depende da ordem em que são executados. Esses dois testes usam uma lista compartilhada:

store = []

def test_add():
    store.append("x")
    assert len(store) == 1

def test_empty():
    assert store == []
Pedido test_add test_empty
vazio e adicione Passe Passe
adicione e depois esvazie Passe Falha

Nossa execução confirmou isso, e é por isso que uma suíte pode passar meses e depois quebrar quando alguém adiciona execuções paralelas ou um plugin de pedido aleatório. A cura é o isolamento:

  • Forneça dados atualizados a cada teste, usando um acessório que os configura e limpa. Nosso tutorial pytest mostra como.
  • Crie registros exclusivos, como um email com um sufixo aleatório, no lugar de um “usuário de teste” compartilhado.
  • Envolva testes de banco de dados em uma transação e reverta-a, onde sua configuração permitir.
  • Execute testes regularmente em ordem aleatória, para que esses problemas apareçam mais cedo.

Causa 3: Datas, fusos horários e aleatoriedade

Um teste que passa a semana toda pode ser reprovado no sábado, à meia-noite ou no último dia do mês. Faça do tempo uma entrada, em vez de ler o relógio dentro da sua lógica:

def is_weekend(today=None):
    today = today or datetime.date.today()
    return today.weekday() >= 5

is_weekend(datetime.date(2026, 11, 7))   # True  (a Saturday)
is_weekend(datetime.date(2026, 11, 9))   # False (a Monday)

Esses testes dão a mesma resposta em qualquer dia. Da mesma forma, defina uma semente fixa para valores aleatórios em testes e registre-a ao usar dados aleatórios para poder reproduzir uma falha. Não assuma uma ordem específica de conjuntos, dicionários de fontes externas ou consultas de banco de dados sem ORDER BY.

Causa 4: Seletores frágeis

Um teste que encontra um botão com div:nth-child(3) > span quebra com qualquer ajuste de layout e pode falhar apenas para alguns tamanhos de tela ou variantes. Prefira localizadores baseados no que os usuários veem ou atributos de teste dedicados. Nosso guia para Localizadores de dramaturgos explica a ordem para testá-los. As animações são outra fonte frequente de problemas, então considere desativá-las em ambientes de teste.

Causa 5: Serviços e redes externas

Se um teste chamar um provedor de pagamento real ou uma API de terceiros, seu resultado dependerá do tempo de atividade e dos limites de taxa de outra pessoa. Simule essas chamadas para a maioria dos testes e mantenha um pequeno número de verificações de contrato separadas em relação ao serviço real, para que você ainda saiba se ele mudar. Nosso guia para testes de unidade versus testes de integração explica como dividi-los.

Dica: “Aprovado localmente, falha no CI” geralmente significa uma diferença de velocidade, fuso horário, recursos ou versões. A execução de testes na mesma imagem de contêiner localmente e no CI remove muitos deles. Nosso tutorial do Docker Compose pode ajudar.

Como diagnosticar um teste escamoso

  1. Confirme. Execute o teste várias vezes sem alterações. Com o pytest, um simples loop de shell funciona, e um plugin como pytest-repeat fornece --count=100.
  2. Registre evidências. Salve logs, capturas de tela e rastreamentos para cada falha. O visualizador de rastreamento do Playwright é particularmente útil aqui.
  3. Enfatize as condições. Execute-o com CPU limitada, em paralelo ou em ordem aleatória. Fazê-lo falhar com mais frequência torna mais fácil estudar.
  4. Teste sozinho. Se ele sempre passar sozinho, mas falhar no conjunto, suspeite do estado compartilhado.
  5. Procure padrões. Verifique quais máquinas, horários do dia e testes vizinhos estão envolvidos.
  6. Verifique o produto. Pergunte honestamente se isso poderia ser uma condição de corrida real em sua aplicação.

As tentativas automáticas são uma solução?

As novas tentativas são uma rede de segurança útil a curto prazo e não são uma cura. Uma única nova tentativa automática no CI pode manter o trabalho em andamento enquanto você investiga, mas um teste que precisa de uma nova tentativa para ser aprovado ainda está lhe dizendo algo. Pior ainda, um bug real e intermitente do produto que falha 20% das vezes geralmente passa por uma nova tentativa despercebido.

Use novas tentativas com estas regras: limite-as a uma ou duas, registre todos os testes que precisaram de uma e trate essa lista como sua lista de tarefas pendentes. Um teste relatado como “aprovado na nova tentativa” deve contar como um problema a ser investigado, e não como um sucesso.

Um processo de equipe para testes instáveis

  1. Avalie. Rastreie quais testes falham e depois passe o mesmo commit. Muitas ferramentas de CI e repórteres de teste podem mostrar isso.
  2. Quarentena. Retire um teste instável confirmado da execução de bloqueio, para que ele pare de atrapalhar a todos, mas continue executando-o e monitorando-o.
  3. Atribua um proprietário e um prazo. Uma quarentena sem prazo vira apenas um cemitério.
  4. Corrija ou exclua. Se não vale a pena corrigir um teste, remova-o e verifique se sua cobertura ainda existe em outro lugar.
  5. Prevenir. Execute novos testes várias vezes antes de mesclar. Um teste que falha uma vez em cinquenta execuções é melhor detectado agora do que em seis meses.

Trate um teste instável como um bug com prioridade, e não como ruído de fundo. A gravidade e a prioridade também se aplicam aqui, conforme abordado em gravidade versus prioridade. Se a instabilidade for generalizada, leia sobre erros mais amplos de automação de testes e considere se você tem muitos testes no nível do navegador.

Considerações finais

Os testes instáveis ​​parecem aleatórios, mas raramente são. Quase todos se resumem a uma suposição sobre tempo, ordem, dados ou ambiente que nem sempre é verdadeira. Escolha o teste que falha com mais frequência em seu pipeline esta semana. Execute-o cinquenta vezes, encontre a suposição e corrija-a para sempre. Uma solução real ensina a uma equipe mais do que cem repetições.

Perguntas frequentes

O que é um teste instável?

Um teste instável é aprovado em algumas execuções e falha em outras, sem qualquer alteração no código. A causa pode estar no teste, no ambiente ou no produto.

O que causa testes instáveis?

As causas mais comuns são suposições de temporização, estado compartilhado entre testes, seletores instáveis, serviços externos, manipulação de datas e diferenças entre CI e máquinas locais.

Devo excluir testes instáveis?

Primeiro tente corrigi-los. Se um teste tiver baixo valor e for caro para corrigir, não há problema em excluí-lo, mas certifique-se de que sua cobertura importante exista em outro lugar.

Devo tentar novamente os testes que falharam automaticamente?

Como uma rede de segurança limitada, sim, mas registre cada nova tentativa e investigue-a. As novas tentativas por si só ocultam problemas e podem permitir que bugs intermitentes reais cheguem à produção.

Como encontro testes instáveis?

Procure testes que falham e passem no mesmo commit, usando seu histórico de CI ou uma ferramenta de relatório. Você também pode executar testes suspeitos várias vezes em um loop.

Continue aprendendo

Trabalhe com Stack Quora

Precisa de ajuda com testes, WordPress ou SEO?

Ajudamos as equipes a fornecer software melhor e a expandir seus sites. Conte-nos no que você está trabalhando e sugeriremos um próximo passo claro.

Continue lendo

Mais guias sobre testes, IA e desenvolvimento moderno

Gostou deste artigo? Escolha um tópico e vá direto para nossos tutoriais mais recentes.

Inteligência artificial

Explicação das alucinações de IA: por que os modelos de linguagem inventam as coisas e como reduzi-las

Explicação das alucinações de IA: o que são, por que os modelos de linguagem as produzem, tipos comuns, como reduzi-las com aterramento e instruções…

Leia o artigo →
DevOps

Variáveis de ambiente explicadas: arquivos .env, segredos e configuração bem feitos

Variáveis de ambiente explicadas com exemplos de Python que executamos: leitura e configuração, por que cada valor é uma string, arquivos .env, hera…

Leia o artigo →
Desenvolvimento de software

Explicação da limitação de taxa de API: algoritmos, cabeçalhos e tratamento de erros 429

Limitação de taxa de API explicada com uma simulação que executamos: token bucket, janela fixa, janela deslizante, 429 respostas, cabeçalhos de limi…

Leia o artigo →
Automação de testes

Zombando em Python: unittest.mock, patch e os erros que fazem os testes mentirem

Zombaria em Python com unittest.mock, testado: Mock e return_value, side_effect, patch onde um nome é usado, especificação e autospec, zombaria de d…

Leia o artigo →
Teste de software

Cobertura de código explicada: cobertura de linha versus filial e o que 100% realmente significa

Cobertura de código explicada com uma execução cobertura.py real: cobertura de linha, ramal e condição, leitura de um relatório, por que 100% de cob…

Leia o artigo →
Bancos de dados e SQL

DELETE vs TRUNCATE vs DROP em SQL: diferenças, reversão e quando usar cada um

DELETE vs TRUNCATE vs DROP explicado com SQL testado: o que cada um remove, rollback, aciona, redefinição de identidade, velocidade e como PostgreSQ…

Leia o artigo →
Bancos de dados e SQL

Exibições SQL explicadas: crie, use e evite as armadilhas, com exemplos

Visualizações SQL explicadas com consultas que executamos: criação de uma visualização, resultados ao vivo, ocultação de colunas, desempenho, visual…

Leia o artigo →
Desenvolvimento de software

Interface TypeScript vs tipo: diferenças reais, testadas com o compilador

Interface TypeScript versus tipo, verificada com o compilador real: o que ambos podem fazer, o que apenas um pode fazer, mesclagem de declarações, e…

Leia o artigo →
Desenvolvimento de software

Python *args e **kwargs explicados: funções flexíveis com exemplos claros

Python *args e **kwargs explicados com o código que executamos: empacotamento e descompactação, ordem dos parâmetros, argumentos somente de palavras…

Leia o artigo →
Desenvolvimento de software

Funções Python Lambda: sintaxe, usos reais e quando não usá-los

Funções lambda do Python explicadas com o código que executamos: sintaxe, classificação com chave, mapa e filtro, fechamentos, armadilha de ligação …

Leia o artigo →
Inteligência artificial

O que são agentes de IA? Como eles funcionam, com um loop de agente simples que você pode executar

O que são agentes de IA? Um guia simples para o loop do agente, ferramentas, memória e proteções, com um pequeno loop Python que executamos, riscos …

Leia o artigo →
DevOps

Monitoramento versus observabilidade: a verdadeira diferença, com logs, métricas e rastreamentos

Explicação do monitoramento versus observabilidade: falhas conhecidas e desconhecidas, logs, métricas e rastreamentos, um passo a passo do incidente…

Leia o artigo →
DevOps

Docker Volumes vs Bind Mounts: persista os dados do contêiner da maneira certa

Volumes Docker versus montagens de ligação explicadas: por que os dados do contêiner desaparecem, volumes nomeados, montagens de ligação, tmpfs, exe…

Leia o artigo →
Automação de testes

TDD vs BDD: principais diferenças, um recurso criado de ambas as maneiras e quando usar cada um

TDD vs BDD explicado com um recurso de desconto construído em ambos os sentidos: refatoração vermelho-verde com o código que executamos, cenários da…

Leia o artigo →
Teste de software

Teste de aceitação do usuário (UAT): processo, lista de verificação e exemplos reais

Explicação do teste de aceitação do usuário: quem o executa, como ele difere do controle de qualidade, um processo de sete etapas, um modelo de caso…

Leia o artigo →
Bancos de dados e SQL

Valores SQL NULL explicados: IS NULL, COALESCE, NULLIF e as armadilhas a serem evitadas

Valores SQL NULL explicados com consultas que executamos: lógica de três valores, IS NULL, COALESCE, NULLIF, como agregações, junções e classificaçã…

Leia o artigo →
Bancos de dados e SQL

Instrução SQL CASE WHEN: sintaxe, exemplos reais e erros comuns

SQL CASE WHEN explicado com consultas que executamos: formulários simples e pesquisados, ordem de condição, pivôs com SUM, truques COUNT, classifica…

Leia o artigo →
Desenvolvimento de software

O que é JWT? JSON Web Tokens explicados com um exemplo prático

O que é JWT? JSON Web Tokens explicados: as três partes, um token construído e adulterado no código que executamos, JWT vs sessões, armazenamento, t…

Leia o artigo →
Desenvolvimento de software

Loop de eventos JavaScript explicado: pilha de chamadas, microtarefas e temporizadores com exemplos

O loop de eventos JavaScript explicado com o código que executamos: pilha de chamadas, filas de tarefas e microtarefas, por que setTimeout 0 não é i…

Leia o artigo →
Desenvolvimento de software

Geradores Python e rendimento explicado: economize memória com exemplos reais

Geradores Python e rendimento explicados com o código que executamos: como eles pausam e reiniciam, medem economia de memória, pipelines, sequências…

Leia o artigo →
Inteligência artificial

O que são incorporações? Similaridade de vetores e cossenos explicada de forma simples

O que são incorporações? Um guia em linguagem simples para vetores, similaridade de cossenos e pesquisa semântica, com um pequeno exemplo que execut…

Leia o artigo →
DevOps

Implantação azul-verde vs canário: diferenças, compensações e como escolher

Explicação da implantação azul-verde versus canário: como cada um funciona, custo, risco e reversão, agrupamento estável de usuários que testamos, m…

Leia o artigo →
Automação de testes

Testes instáveis: o que os causa e como corrigi-los para sempre

Testes instáveis explicados com demonstrações que executamos: as principais causas, como confirmar e diagnosticar uma, soluções adequadas para tempo…

Leia o artigo →
Teste de software

Teste de carga versus teste de estresse: diferenças, métricas e exemplos k6

Teste de carga versus teste de estresse explicado: o que cada um prova, testes de pico e absorção, as métricas que importam, por que as médias engan…

Leia o artigo →
Teste de software

Caso de teste versus cenário de teste: principais diferenças, exemplos e quando usar cada um

Caso de teste vs cenário de teste explicado: definições claras, uma tabela lado a lado, um recurso de redefinição de senha retirado de cenários para…

Leia o artigo →
Bancos de dados e SQL

Otimização de consultas SQL: leia planos de execução e corrija consultas lentas, com intervalos

Otimização de consulta SQL testada em um milhão de linhas: leia planos de execução, adicione os índices corretos, corrija filtros não sargáveis, use…

Leia o artigo →
Bancos de dados e SQL

SQL UNION vs UNION ALL: diferenças, exemplos e desempenho testado

SQL UNION vs UNION ALL explicado com consultas que executamos: duplicatas, as regras que ambos seguem, um bug de dados real, desempenho medido, INTE…

Leia o artigo →
Desenvolvimento de software

Tutorial da API JavaScript Fetch: GET, POST, tratamento de erros e tempos limite

Um tutorial da API JavaScript Fetch testado em um servidor local: GET e POST, por que 404 não lança, um auxiliar reutilizável, strings de consulta, …

Leia o artigo →
Desenvolvimento de software

Manipulação de arquivos Python: leitura, gravação, CSV, JSON e pathlib com exemplos

Manipulação de arquivos Python explicada com código que executamos: modos abertos, com, leitura de arquivos grandes, CSV, JSON, pathlib, arquivos bi…

Leia o artigo →
Desenvolvimento de software

Classes e objetos Python: tutorial OOP com exemplos reais

Classes e objetos Python explicados com código que executamos: __init__, métodos, atributos de classe vs instância, herança, propriedades, métodos d…

Leia o artigo →
Inteligência artificial

O que é um modelo de linguagem grande? Como funcionam os LLMs, explicado sem exageros

O que é um modelo de linguagem grande? LLMs explicados em linguagem simples: tokens, incorporações, atenção, treinamento, temperatura e janelas de c…

Leia o artigo →
Desenvolvimento de software

Git merge vs rebase: diferenças, exemplos e quando usar cada um

Git merge vs rebase explicado com saída real do log, uma tabela comparativa, a regra de ouro do rebase e um fluxo de trabalho seguro para equipes.

Leia o artigo →
Desenvolvimento de software

List comprehension em Python: sintaxe, exemplos e quando evitar

List comprehension em Python explicada: sintaxe, filtros, if-else, laços aninhados, tempos medidos e quando um laço for comum é a melhor escolha.

Leia o artigo →
Bancos de dados e SQL

SQL JOIN explicado com exemplos (Inner, Left, Right, Full)

Entenda os joins SQL passo a passo com tabelas e consultas de exemplo: INNER, LEFT, RIGHT, FULL, CROSS e SELF, além de erros comuns e dicas de desem…

Leia o artigo →