FIRST: Um jeito simples de validar a qualidade dos seus testes
Thiago Brito
16 de Novembro de 2020 4 minutos de leitura

Testes, assim como o código de produção devem ter uma qualidade impecável, ninguém deve se dar ao luxo de criar testes que trazem mais preocupações ou dúvidas quando o código testado está com problemas ou não.

Trabalhar com testes de má qualidade, com certeza cobrará o preço de ter uma bateria não confiável e que traz uma série de dificuldades a mais para o time.

Os testes se provam quando informam ao desenvolvedor que as alterações que acabaram de serem feitas não batem com as especificações dadas. Um dos sinais de que algo está errado é quando um teste quebra e o time fica confuso sobre onde exatamente ocorreu o problema e acabam gastando um tempo significativo entendendo a real situação.

Sendo assim, aguardar estas péssimas situações acontecerem não costuma ser uma boa idéia. Muito melhor seria se, quando os testes e o código estão sendo criados, termos a capacidade de mensurar se a qualidade dos testes especificamente estão bons ou não.

Então, para identificar isso, uma forma bastante interessante é seguir os princípios contidos no FIRST, que se bem utilizados, trazem clareza e ajudam a levar a qualidade dos seus testes para um outro nível.

Eu vi a definição sobre esta técnica no livro Clean Code e apliquei diversas vezes no meu dia a dia, posso dizer que realmente funciona! Então vamos lá:

F (Fast)

Seus testes devem ser executados com frequência, ou seja, toda vez que o seu código é alterado, toda a bateria tem que ser validada em poucos segundos. Caso eles sejam lentos, naturalmente você vai pensar duas vezes antes de rodar a bateria inteira.

Neste caso, a chance de trabalhar usando Baby Steps é reduzida e várias mudanças vão sendo feitas entre uma verificação e outra, o que aumenta significamente as chances de gastarmos um tempo enorme tentando entender qual alteração realmente causou o problema.

Sendo assim, fazer com que os testes sejam rápidos faz com que o custo de execução deles seja mínimo e obviamente aumenta a qualidade e segurança em cada alteração realizada.

Por isso, use todas as ferramentas para medir e garantir que sua bateria de testes está o mais rápido possível. E, se for o caso, reescreva testes muito lentos ou isole eles para serem executados em momentos posteriores.

I (Isolated)

Imagine que você executa toda a bateria de testes e está tudo bem. Porém, quando somente um teste é executado isoladamente tudo se quebra, ou pior, se um teste é comentado, uma série de outros acabam quebrando sem motivo algum.

Descobrir este tipo de problema é um pesadelo, afinal como a dependencia entre os testes e grande provavelmente o código pode não estar muito claro e falsos positivos podem estar aparecendo sem qualquer aviso.

Garanta que seus casos de testes não dependam um do outro e garanta também que ele não dependa de internet, conexão com banco de dados externo, serviço de e-mail e qualquer coisa que esteja fora da máquina em que o teste está sendo executando. Tirando algumas exceções não dependa nem do sistema operacional.

Para fazer isso, saber utilizar bem mocks, injeção de dependencias, setup/teardown e organizar bem seu código para facilitar a criação de testes independentes é crucial.

Somente com este princípio a tendência é que sua arquitetura fique naturalmente mais limpa.

R (Repeatable)

Seus testes devem ter os mesmos resultados independente de onde, quando ou como você execute.

Este é um conceito extremamente importante. Testes que trazem resultados diferentes a cada execução tendem a não serem confiáveis e trazer dores de cabeça enormes para o time.

Certa vez, tinhamos um conjunto de testes que quebravam sem qualquer motivo e a resposta padrão do time era "roda novamente até que os testes passem". Nem preciso dizer que, um dia houve um erro no código e os testes eram capazes de capturar ele, mas como já era normal estes eles falharem de tempos em tempos, seus resultados foram ignorados e um bug nasceu.

Um teste inconsistente é pior do que não ter qualquer teste. Estes tipos trazem desinformação para o time, fazendo barulho onde não precisa e enganando os desenvolvedores dando alarmes falsos ou verdadeiros que são ignorados.

Se houver algum teste que não está consistente, analise se ele está bem isolado do ambiente e garanta que eles sejam corrigidos o mais breve possível e, caso isso não seja prioridade agora, considere desativa-los temporariamente.

Também fique de olho com testes que manipulem datas, conexões com serviços externos, arquivos criados por outros serviços e muita atenção a testes que checam funções assincronas.

S (Self-validating)

Hoje em dia, se você utilizar qualquer framework de Unit Tests isso já vem por padrão. O conceito aqui é que, no final da execução, ele mesmo deve responder de forma bastante clara se os testes passaram ou não.

Não deve existir qualquer validação manual para entender o resultado dos testes. Analisar estas informações deve ser simples, e você consegue resolver isso fácilmente com uma boa IDE e um Framework de testes onde você será capaz de saber exatamente a checagem que quebrou com apenas alguns cliques.

T (Thorough)

Seus testes devem ser completos, ele deve cobrir os caminhos felizes, tratar as exceções disparadas e também o máximo de casos especiais que puder imaginar.

Não confunda isso acreditando que os testes devem ser complexos. Eles devem ser completos, busque sempre cobrir o máximo possível do código.

Este princípio faz com que qualquer pessoa que for fazer mudanças tenha segurança de que, o que já estava funcionando antes continue assim após as alterações.

Quando os testes não estão completos, pode trazer uma falsa segurança para quem está fazendo o refactoring, ou pior, quando isso é detectado e não solucionado rapidamente, pode trazer a efeito das janelas quebradas.

Sendo assim, veja e aplique estes princípios em seus testes, durante a criação de cada caso de teste faça uma análise se está aderente ao FIRST, isso fará com que você tenha um salto de qualidade enorme dentro da sua base de código.