Olá, colega desenvolvedor! Se você já trabalhou com testes automatizados, conhece a sensação de aflição ao ver um teste falhar, mesmo que nada tenha mudado no código. Vamos criar um cenário, aposto que é tudo muito familiar. Você envia seu código lindamente elaborado, confiante de que é o seu melhor trabalho até agora. Você aciona o pipeline de integração contínua (CI) e espera por aquela satisfatória marca de verificação verde. Mas, em vez disso, você recebe um grande e irritado X vermelho. Seu coração afunda. "O que eu quebrei?!" Você verifica freneticamente os logs, apenas para encontrar... uma falha de teste aleatória. Você o executa novamente: às vezes passa, às vezes não.
Parece familiar? Você, meu amigo, acabou de ser vítima de um teste intermitente (flaky test).
E aqui está a verdade: testes intermitentes (flaky tests) desperdiçam tempo do desenvolvedor, atrasam os pipelines de CI/CD e criam uma frustração enorme entre as equipes. Testes intermitentes são os poltergeists assombradores do desenvolvimento de software. Eles falham de forma imprevisível e aparentemente aleatória, corroendo a confiança em todo o seu processo de teste, desperdiçando incontáveis horas de investigação e desacelerando a entrega a um ritmo glacial. Na verdade, são um ponto de dor tão universal que líderes da indústria como o Google publicaram pesquisas extensas sobre como eliminá-los.
Mas aqui está a boa notícia: testes intermitentes não são mágica. Eles têm causas específicas e identificáveis. E o que pode ser identificado pode ser corrigido. Você pode lidar com eles assim que entender suas causas-raiz.
Quer uma plataforma integrada e completa para sua Equipe de Desenvolvedores trabalhar com máxima produtividade?
Apidog entrega todas as suas demandas e substitui o Postman por um preço muito mais acessível!
botão
O Que Exatamente É um Teste Intermitente (Flaky Test), Afinal?
Antes de listarmos os culpados, vamos definir nosso inimigo. Um teste intermitente (flaky test) é um teste que exibe comportamento de sucesso e falha quando executado múltiplas vezes na mesma versão idêntica do código. Não é um teste que falha consistentemente por causa de um bug. É um teste que falha inconsistentemente, tornando-o um indicador ruidoso e não confiável da saúde do código.
Por exemplo:
- Execução #1 → ✅ Sucesso
- Execução #2 → ❌ Falha
- Execução #3 → ✅ Sucesso novamente
O custo desses testes é imenso. Eles levam a:
- O Ciclo "Reexecutar e Rezar": Desperdício de recursos de desenvolvedores e de CI.
- Fadiga de Alerta: Quando os testes falham frequentemente sem motivo, as equipes começam a ignorar as falhas, o que significa que bugs reais passam despercebidos.
- Velocidade de Desenvolvimento Mais Lenta: Builds quebrados e tempo de investigação atrasam toda a equipe.
Por Que Testes Intermitentes São Perigosos Para as Equipes
Você pode pensar: "É apenas um teste falhando, vou reexecutá-lo." Mas aqui está o problema:
- Perda de Confiança → Desenvolvedores param de confiar nos resultados dos testes.
- CI/CD Mais Lento → Pipelines ficam congestionados com retentativas.
- Bugs Ocultos → Problemas reais são ignorados porque as pessoas assumem "ah, é apenas intermitente."
- Custos Aumentados → Mais reexecuções significam mais tempo, recursos e infraestrutura.
De acordo com estudos da indústria, algumas empresas gastam até 40% do tempo de teste lidando com a intermitência. Isso é enorme!
Agora, vamos conhecer os suspeitos de sempre.
As Causas e Correções de Testes Intermitentes
1. Operações Assíncronas e Condições de Corrida
Este é, sem dúvida, o rei dos testes intermitentes. Em aplicações modernas, tudo é assíncrono: chamadas de API, operações de banco de dados, atualizações de UI. Se o seu teste não espera corretamente que essas operações sejam concluídas, ele está essencialmente adivinhando. Às vezes, ele adivinha certo (a operação termina rápido), e às vezes adivinha errado (é lento), levando a uma falha.
Por que acontece: Seu código de teste é executado de forma síncrona, mas o código da aplicação que ele está testando não é.
Exemplo: Um teste que clica em um botão "Salvar" e imediatamente verifica o banco de dados para o novo registro sem esperar que a requisição de rede da operação de salvamento seja concluída.
A Correção:
- Use Esperas Explícitas: Nunca use chamadas estáticas
sleep()
ousetTimeout()
. Estas são uma fonte primária de intermitência porque você está esperando demais (atrasando os testes) ou não o suficiente (causando falhas). - Empregue Estratégias de Espera: Use ferramentas que permitam esperar por uma condição específica. Por exemplo:
- Testes de UI: Espere que um elemento esteja visível, clicável ou contenha um texto específico.
- Testes de API: Espere por um status de resposta HTTP específico ou por um payload aparecer no banco de dados.
- Selenium/WebDriver: Use
WebDriverWait
comexpected_conditions
. - Cypress: O Cypress possui espera automática embutida para a maioria dos comandos, o que é fantástico para evitar este problema.
2. Problemas de Isolamento de Teste
Os testes devem ser como estranhos educados: não devem deixar bagunça para a próxima pessoa. Quando os testes compartilham estado e não se limpam, eles podem facilmente interferir uns nos outros. O Teste A cria um usuário "test@example.com", passa, mas não o exclui. O Teste B então tenta criar o mesmo usuário e falha devido a uma violação de restrição única.
Por que acontece: Recursos compartilhados como bancos de dados, caches ou sistemas de arquivos são modificados por um teste, alterando o estado inicial para o próximo teste.
A Correção:
- Garanta Isolamento Total: Cada teste deve configurar seus próprios dados necessários e desfazê-los completamente depois. Esta é a regra de ouro.
- Use Transações: Um padrão poderoso é executar cada teste dentro de uma transação de banco de dados e, em seguida, revertê-la no final. Isso deixa o banco de dados completamente intocado.
- Gere Dados Únicos: Use identificadores únicos (como UUIDs ou timestamps) nos dados de teste para evitar conflitos. Por exemplo,
test.user.<timestamp>@example.com
.
3. Dependências de Serviços Externos
Sua suíte de testes chama uma API de terceiros para processamento de pagamentos, dados meteorológicos ou validação de e-mail? Se sim, você introduziu um enorme ponto de falha que está inteiramente fora do seu controle. Essa API pode estar lenta, aplicando limites de taxa, fora do ar para manutenção ou ter alterado ligeiramente seu formato de resposta — tudo isso fará com que seus testes falhem sem culpa sua.
Por que acontece: O sucesso do teste está acoplado à saúde e desempenho de um sistema externo.
A Correção:
- Mocar e Stubear Serviços Externos: Esta é a estratégia mais importante. Em vez de fazer uma chamada HTTP real, intercepte a requisição e retorne uma resposta falsa e predeterminada que simule um caso de sucesso ou erro.
- Use Ferramentas para Mocking: É aqui que o Apidog se destaca. O Apidog permite que você crie mocks poderosos para suas APIs. Você pode definir exatamente qual resposta uma API deve retornar para uma dada requisição, eliminando completamente a dependência do serviço externo real e intermitente. Seus testes se tornam rápidos, confiáveis e previsíveis.
- Use Virtualização de Serviço: Para cenários mais complexos, ferramentas que simulam o comportamento de sistemas externos inteiros podem ser usadas.
4. Dados de Teste Não Gerenciados
Semelhante aos problemas de isolamento, mas mais amplo. Se seus testes assumem um estado específico do banco de dados (por exemplo, "deve haver exatamente 5 usuários" ou "um produto com ID 123 deve existir"), eles falharão no momento em que essa suposição for falsa. Isso geralmente acontece com testes executados em um banco de dados de desenvolvimento ou staging compartilhado que está em constante mudança.
Por que acontece: Os testes fazem suposições implícitas sobre o estado dos dados do ambiente.
A Correção:
- Gerencie Explicitamente Todos os Dados: Um teste nunca deve assumir nada sobre o mundo. Ele deve criar todos os dados de que precisa para ser executado.
- Use Factories e Fixtures: Bibliotecas como
factory_bot
(Ruby) ou padrões semelhantes em outras linguagens ajudam você a gerar facilmente os dados precisos necessários para cada teste. - Evite IDs Hard-Coded: Nunca dependa da existência de um ID de registro específico. Crie o registro e use seu ID gerado em suas asserções de teste.
5. Concorrência e Execução de Testes Paralelos
Executar testes em paralelo é essencial para a velocidade. No entanto, se seus testes não forem projetados para isso, eles se atropelarão. Dois testes executando ao mesmo tempo podem tentar acessar o mesmo arquivo, usar a mesma porta em um servidor local ou modificar o mesmo registro de banco de dados.
Por que acontece: Os testes são executados simultaneamente, mas foram escritos com a suposição de que seriam executados sozinhos.
A Correção:
- Projete para Paralelismo Desde o Início: Assuma que os testes serão executados em paralelo.
- Isole Recursos: Garanta que cada executor de teste paralelo tenha seu próprio ambiente isolado: um esquema de banco de dados único, uma porta única para servidores locais, etc.
- Use Operações Seguras para Threads: Esteja atento a qualquer estado compartilhado na memória.
6. Dependência do Tempo do Sistema
"Este teste falha depois das 17h?" Parece bobo, mas acontece. Testes que usam o tempo real do sistema (new Date()
, DateTime.Now
) podem se comportar de forma diferente dependendo de quando são executados. Um teste verificando se um "relatório diário" foi gerado pode passar quando executado uma vez às 23h59 e falhar quando executado novamente dois minutos depois, à 00h01.
Por que acontece: O relógio do sistema é uma entrada externa e mutável.
A Correção:
- Mocar o Tempo: Use bibliotecas que permitam "congelar" ou "viajar" no tempo. Bibliotecas como
timecop
(Ruby),freezegun
(Python) oumockStatic
doMockito
parajava.time
(Java) permitem que você defina um horário específico para seu teste, tornando-o completamente determinístico.
7. Código Não Determinístico em Testes
Este é sutil. Se o código sob teste for não determinístico (por exemplo, usa um gerador de números aleatórios ou embaralha uma lista), seu teste não poderá fazer uma asserção consistente sobre sua saída.
Por que acontece: A própria lógica da aplicação possui aleatoriedade.
A Correção:
- Seed Geradores de Números Aleatórios: A maioria dos geradores de números aleatórios pode ser semeada com um valor fixo. Isso torna a sequência de números "aleatórios" idêntica a cada vez, tornando o teste determinístico.
- Teste o Comportamento, Não a Implementação: Em vez de fazer uma asserção sobre a saída exata de uma função
shuffle()
(que é, por definição, aleatória), faça uma asserção sobre o comportamento. Por exemplo, afirme que a lista de saída contém todos os mesmos elementos da lista de entrada, apenas em uma ordem diferente. Ou, mocar a função shuffle para retornar uma ordem fixa durante o teste.
8. Seletores de UI Frágeis
Esta é a clássica intermitência de testes de front-end. Seu teste encontra um elemento na página usando um seletor CSS como #main > div > div > div:nth-child(3) > button
. Um desenvolvedor então ajusta ligeiramente a estrutura HTML adicionando uma nova div
para estilização e, boom, seu seletor está quebrado, mesmo que a funcionalidade esteja perfeitamente bem.
Por que acontece: Os seletores estão muito acoplados à estrutura do DOM, que é volátil.
A Correção:
- Use Localizadores Robustos: Priorize seletores que são menos propensos a mudar.
- Melhor: Use um atributo
data-testid
dedicado (por exemplo,<button data-testid="sign-up-button">
). Isso desvincula o teste da estilização e da estrutura. - Bom: Use IDs (
#submit-button
), mas apenas se forem estáveis e não forem usados para CSS. - Aceitável: Use funções ARIA ou conteúdo de texto, mas esteja atento à internacionalização e às mudanças de texto.
- Evite: Caminhos CSS/XPath complexos e aninhados baseados na estrutura.
9. Vazamentos de Recursos e Falhas de Limpeza
Testes que não fecham corretamente os recursos podem fazer com que testes subsequentes falhem de maneiras estranhas. Isso pode ser deixar conexões de banco de dados abertas, não fechar instâncias de navegador ou não excluir arquivos temporários. Eventualmente, o sistema fica sem recursos, causando timeouts ou falhas.
Por que acontece: O código de teste não possui lógica de teardown/limpeza adequada.
A Correção:
- Use Hooks
beforeEach
/afterEach
: Estruture seus testes para sempre limpar em uma fase de teardown dedicada, mesmo que o teste falhe. A maioria dos frameworks de teste oferece hooks para isso. - Empregue os Padrões Corretos: Use padrões como a declaração
using
(C#) outry-with-resources
(Java) para garantir que os recursos sejam fechados automaticamente.
10. Inconsistências de Ambiente
"O teste funciona na minha máquina!" Este clássico lamento é frequentemente causado pela intermitência do ambiente. Diferenças em sistemas operacionais, versões de navegador, versões de Node.js, bibliotecas instaladas ou variáveis de ambiente entre a máquina local de um desenvolvedor e o servidor de CI podem fazer com que os testes falhem de forma imprevisível.
Por que acontece: O ambiente de teste não é reproduzível.
A Correção:
- Containerize Tudo: Use Docker para definir seu ambiente de teste. Um
Dockerfile
garante que cada execução de teste — local e CI — ocorra em um ambiente idêntico e controlado. - Fixe a Versão de Tudo: Use
package-lock.json
,Gemfile.lock
,Pipfile.lock
, etc., para fixar as versões exatas de todas as suas dependências. - Gerencie a Configuração de Forma Segura: Use um método consistente e seguro para lidar com variáveis de ambiente e segredos necessários para o teste.
Como Detectar Testes Intermitentes em Seu Pipeline
Detectar testes intermitentes precocemente é fundamental. Aqui estão as estratégias:
- Reexecute testes automaticamente → Se um teste passar após a reexecução, marque-o como intermitente.
- Rastreie padrões de falha → Logs de CI/CD frequentemente revelam testes intermitentes recorrentes.
- Isole testes intermitentes → Marque-os e execute-os separadamente até que sejam corrigidos.
- Use ferramentas de monitoramento → Ferramentas como Jenkins, CircleCI e GitHub Actions podem relatar a intermitência de testes.
Reduzindo Testes Intermitentes com Apidog

Como muitos testes intermitentes estão relacionados a APIs e dependências externas, o Apidog ajuda você a:
- Criar servidores mock para que você não dependa de APIs reais instáveis.
- Automatizar cenários de teste com resultados previsíveis.
- Executar testes de desempenho para ver como as APIs se comportam sob estresse.
- Centralizar todos os seus testes de API para que você possa detectar comportamentos intermitentes precocemente.
Em vez de depurar falhas aleatórias às 2 da manhã, você saberá exatamente se é seu código ou uma dependência externa intermitente.
botão
Melhores Práticas para Evitar Testes Intermitentes
Aqui está uma lista de verificação rápida para reduzir a intermitência dos testes:
- Escreva testes determinísticos com resultados previsíveis.
- Use mocks/stubs para APIs e redes.
- Evite atrasos codificados use esperas orientadas por eventos.
- Redefina os ambientes de teste entre as execuções.
- Monitore os testes ao longo do tempo para identificar padrões intermitentes.
- Documente testes intermitentes conhecidos para que a equipe esteja ciente.
Construindo uma Cultura Contra a Intermitência
Corrigir testes individuais é uma coisa; preveni-los é outra. Requer uma cultura de equipe que valorize a confiabilidade dos testes.
- Não Tolere a Intermitência: Se um teste for intermitente, coloque-o em quarentena imediatamente. Mova-o para uma suíte separada e não bloqueadora para que não impeça as implantações, mas agende um tempo para corrigi-lo o mais rápido possível.
- Rastreie Testes Intermitentes: Mantenha uma lista visível de testes intermitentes conhecidos e priorize a correção deles.
- Revise Testes em Revisões de Código: Trate o código de teste com a mesma seriedade que o código de produção. Procure os anti-padrões que discutimos durante as revisões.
Conclusão: De Intermitente a Robusto
Testes intermitentes são um dos problemas mais frustrantes no desenvolvimento de software; são um incômodo, mas solucionáveis. Eles desperdiçam tempo, criam desconfiança e atrasam os lançamentos. Ao entender estas 10 principais causas — desde esperas assíncronas e isolamento de testes até mocks externos e seletores frágeis — você ganha o poder não apenas de corrigi-los, mas também de escrever testes mais robustos e confiáveis desde o início, podendo corrigi-los sistematicamente.
Lembre-se, uma suíte de testes é um sistema de alerta precoce crítico para a saúde da sua aplicação. Seu valor é diretamente proporcional à confiança que a equipe de desenvolvimento tem nela. Ao eliminar impiedosamente a intermitência, você reconstrói essa confiança e cria um fluxo de trabalho de desenvolvimento mais rápido e confiante. A melhor estratégia? Projetar testes determinísticos, isolados e bem estruturados.
E para aqueles testes intermitentes particularmente complicados relacionados a APIs, lembre-se que uma ferramenta como o Apidog pode ser seu aliado mais forte. Suas capacidades de mocking e teste são projetadas especificamente para criar o ambiente estável e previsível que seus testes precisam para prosperar. O Apidog pode salvá-lo de um mundo de dor de testes intermitentes, simulando ambientes estáveis. Agora vá em frente e torne sua suíte de testes inquebrável.
botão