Testes que falham de vez em quando são piores do que nenhum
Um teste que falha de vez em quando parece um mal menor: corre-se outra vez e passa. O custo real não é o tempo perdido — é o que ele ensina. Ao fim de duas semanas, a equipa aprendeu que o vermelho não quer dizer nada, e a partir daí a bateria de testes deixou de ser um sinal para passar a ser ruído.
É por isso que são piores do que não ter teste nenhum. Sem testes, ninguém confia; com testes intermitentes, confia-se e não se devia.
1. De onde vêm, por ordem de frequência
Esperar por tempo em vez de esperar por uma condição. É a primeira de todas, e a mais fácil de corrigir:
// Não: 200 ms é um palpite sobre a máquina de quem corre
await esperar(200);
assert.ok(elemento.visivel);
// Sim: espera pelo que interessa, e desiste ao fim de um limite
await ateQue(() => elemento.visivel, { limite: 2000 });Um número de milissegundos escrito à mão acerta na máquina de quem o escreveu e falha na integração contínua, que é mais lenta e está sempre com carga.
Ordem e estado partilhado. Dois testes que escrevem na mesma tabela, no mesmo ficheiro ou na mesma variável de módulo. Passam na ordem por que foram escritos e falham no dia em que o executor decide paralelizar.
Datas de fronteira. O teste que passa o ano inteiro e falha a 31 de dezembro, ou às 23:59, ou num ano bissexto. Aparece uma vez e desaparece antes de alguém o reproduzir.
A rede. Se um teste fala com o mundo, um dia o mundo não responde.
2. Apanhá-lo antes que ele apanhe
Um teste intermitente não se encontra a correr a bateria uma vez. Encontra-se a forçar as condições que o fazem falhar:
# Repetir o mesmo teste muitas vezes seguidas
for i in $(seq 1 100); do npm test -- o-suspeito || break; done
# E correr tudo em paralelo, que é onde o estado partilhado aparece
node --test --experimental-test-isolation=noneSe o executor souber baralhar a ordem, ainda melhor. A maior parte dos casos de estado partilhado cai à primeira mudança de ordem.
3. O que fazer quando aparece um
Há três reações possíveis, e uma delas é má:
Corrigir — o ideal, quando a causa é óbvia.
Pôr de quarentena — marcá-lo como saltado, com um registo aberto e um prazo. Feio, mas honesto: toda a gente sabe que aquilo está por resolver.
Repetir automaticamente até passar — esta é a má, e é a mais usada.
A repetição automática não conserta nada; esconde. E esconde a coisa errada: um teste intermitente é, muitas vezes, um produto intermitente. Uma condição de corrida que falha num teste em cada cinquenta vai falhar em produção em cada cinquenta utilizadores — só que ali ninguém volta a tentar.
4. O que os torna impossíveis
Fui ver a bateria deste site à procura de intermitentes e não há nenhum — mas isso não é mérito, é consequência de onde a lógica ficou:
contarPalavras(html) // mesmo texto, mesmo número
realcarCodigo(html) // mesmo código, mesmas marcas
tomMaisLivre([0, 30, 60]) // mesma lista, mesmo tomNão há relógio, não há rede, não há ordem, não há nada partilhado. Corram na sequência que correrem, dão o mesmo resultado.
Onde isso não for possível — e nem sempre é —, as duas regras que mais rendem são: cada teste cria o que precisa e limpa atrás de si (uma transação com rollback no fim resolve o caso da base), e o que vem de fora entra como argumento, incluindo o relógio.
O resumo
Um teste que falha de vez em quando está a dizer alguma coisa. Quase sempre é «há aqui uma corrida», e a corrida também existe no produto. Repeti-lo até passar é mandar calar a única parte do sistema que reparou no problema.