Testes unitários em front com Typescript?
O CONTEXTO
Já fazem quase 6 meses que o time de tecnologia da minha empresa passou por umas mudanças e decidimos adotar as práticas e métricas de DevOps do DORA como religião. E no meio desse bolo todo de métricas e práticas vem o TDD.No começo, com a gente foi o mesmo que acontece com todo mundo que começa nessa vida de bater coverage: estresse, lágrimas e uma incessante dúvida de "isso tudo tá valendo a pena?". Agora, depois de quase 6 meses, com a prática, já estamos naquela fase onde fazer testes não é mais tão trabalhoso e complicado. Mas na nossa equipe de frontend a dúvida sempre continou: "e isso tudo tá valendo a pena?"
Quem já fez sabe: testes unitários em frontend são muito mais complexos de fazer do que no backend. Isso devido a todo o setup que é necessário preparar para se simular a DOM, o navegador e o usuário. Principalmente devido ao fator "usuário", os testes acabam tendo mais cenários possíveis para se contemplar e cada teste precisa de um ambiente mockado diferente para tentar simular os componentes certos.
A QUESTÃO
O Istambul, ferramenta de coverage usada por nós, metrifica a porcentagem de código testado apenas analisando as linhas de código executadas. Se a linha X do arquivo Y não foi executada em nenhum teste, então significa que ela não foi testada, logo não há garantias de que ela vai funcionar.Isso faz sentido... caso a gente não usasse Typescript.
O Typescript, estritamente configurado e bem utilizado, é capaz de me informar de virtualmente todos os possíveis cenários que podem passar por cada linha. Ele me garante que nunca farei nenhuma operação que possa causar algum erro em qualquer hipótese porque ele é onisciente sobre o código.
Então por que eu preciso criar testes que apenas renderizam componentes e executam funções para checar se eles foram renderizados sem erro? O coverage do Istambul apenas me obriga a gastar tempo montando um setup de mocks para executar uma ação que eu JÁ SEI que vai dar certo. Eu não quero isso e muito menos o cara que paga meu salário quer.
Isso culmina em um cenário muito importante de se ter em mente caso sua equipe esteja entrando no mundo do TDD: seu app pode ostentar 100% de coverage, sua equipe pode se gabar de ter escrito 500 test suits e ainda assim o código não possuir nem 1 teste realmente relevante.
Ainda assim, por algum motivo, todos os lugares por onde a gente pesquisava pareciam idolatrar os testes unitários, mesmo com Typescript, como se fosse uma verdade óbvia.
A CONCLUSÃO
Tendo como premissa que a única variável real é o usuário, já que o TS já estaria testando em tempo real tudo do código pra dentro, chegamos na conclusão de que os únicos testes que realmente valem a pena escrever são os testes do tipo "Evento x Resultado". Ou seja:Após o estímulo X, Y aconteceu?
APENAS.
Isso significa duas coisas muito importantes:
1. Não teste nada que não possua efeito observável.
2. Não teste os meios, mas apenas os fins.
Seguindo esses dois princípios você não só acaba escrevendo testes mais pragmáticos/eficientes, mas também faz com que deixem o código aberto para refatoração. De que interessa se para atualizar a URL meu código usou a função/adapter X? A URL no final foi atualizada conforme o esperado ou não? O resultado final é o que devo garantir que continue acontecendo nos meus testes.
Há ainda um terceiro ponto importante para se ter em mente: nunca teste código dos outros! Isto é, não teste componentes vindos de libs de terceiros. Primero, porque você não é pago pra isso; e segundo, porque você não quer testar o mesmo código várias vezes em diferentes lugares. Ao invés disso, procure instalar dependências que possuam badge de test coverage no repositório, além de sempre ter um SAST/DAST na sua pipe.
Isso não significa que a métrica de coverage não deva existir, porque caso contrário vira várzea. Afinal, ainda não chegamos num consenso de qual é a porcentagem ideal. Mas duas coisas bem importantes foram percebidas pela gente:
1. O coverage não deve ser o guia e símbolo de qualidade de código.
2. Os reviews de PRs devem ser mais atenciosos com os testes que são escritos ou poderiam ter sido.
Assim como os testes unitários ajudam a diminuir a carga dos testes de integração, o Typescript ajuda a diminuir a carga dos testes unitários. Usar typescript (ou qualquer linguagem fortemente tipada) é escrever testes unitários enquanto escreve código.
Não importa há quanto tempo você escreve testes, parece que sempre vai ser uma atividade de alto custo e um retorno não igualmente alto. Isso não significa que você não deve fazer, porque eles FAZEM sim a diferença e só aumentam seu valor conforme seu app aumenta. Eles nunca serão sua garantia de excelente funcionamento, mas são o que fazem você dormir bem mais tranquilo todas as noites sabendo que se algum bug repentino estourar, dificilmente vai ser algo crítico que já não foi testado infinitas vezes antes pelo mesmo teste que você escreveu meses atrás.
E você, concorda com isso?