Cultura de testes no front-end

Gabriel Reichardt Gueiros
afya
Published in
9 min readNov 22, 2023

São inegáveis os benefícios de uma cultura de testes automatizados no desenvolvimento de software, mas antes de seguir com uma ideia mais detalhada de como isso pode aumentar a qualidade do produto e facilitar o dia a dia das equipes vamos dar um passo atrás para estabelecer alguns parâmetros iniciais.

Quando falar de testes, a que estou me referindo? E o que seria uma cultura de testes?

De forma geral, quando citar “testes” daqui pra frente, estou falando de testes automatizados, que basicamente são compostos por código que testa outro código. Uma cultura de testes se forma quando os times de engenharia e produto estão cientes de que esses testes são importantes para o desenvolvimento de um produto de qualidade e os incluem como parte fundamental nas entregas.

Como isso afeta o produto

Antes falarmos do dia a dia de desenvolvimento, é importante entender o impacto dos testes automatizados no software, são inúmeras as vantagens que uma cultura de testes pode trazer para o produto que está sendo desenvolvido, dentre elas podemos destacar:

  • Confiabilidade: A confiança de que erros simples (mas que podem ter um grande impacto) não passem despercebidos.
  • Qualidade: A possibilidade de times diferentes trabalharem de maneira independente com a segurança de que fluxos adjacentes estão cobertos por testes e que se alguma implementação alterar regras sensíveis, todos terão visibilidade.
  • Agilidade: Uma cobertura de testes eficiente permite mais confiança e assertividade em novas implementações tornando as entregas mais rápidas e possibilitando que novas hipóteses sejam validadas com maior eficiência.
  • Tempo: O tempo investido pelo time de engenharia escrevendo testes evita uma quantidade muito maior de tempo na identificação e correção de erros e até em problemas envolvendo o usuário final.

Como uma cultura de testes afeta a experiência de desenvolvimento

Para desenvolvedores em geral é muito comum conviver com um código que poderia ser melhorado, os motivos variam desde uma entrega que precisava ser feita às pressas, passando por mudanças nas decisões de engenharia ou até a própria passagem de tempo que podem tornar nosso código obsoleto, complexo e difícil de ler.

Uma de nossas responsabilidades como codificadores é lidar com esse tipo de situação de maneira assertiva e segura e a principal forma de fazer isso é refatorando o código, ou seja, ( permitam-me a simplificação ) mudando a implementação para fazer com que um trecho de código reflita de maneira legível e direta as decisões de negócio, e é aí que uma cultura de testes pode ajudar — e muito — na experiência de desenvolvimento.

Como podemos garantir que um código complexo e difícil de ler vai continuar se comportando da mesma maneira depois de alterarmos? Ou ainda, será que uma nova implementação vai interferir em algum fluxo existente? Com uma cobertura eficiente de testes podemos garantir que um fluxo funciona conforme o esperado e a partir daí entender muito melhor os impactos das nossas alterações.

Quando já temos uma preocupação com testes desde o início do processo, além de garantir que o nosso código se comporta da maneira que desejamos em vários cenários diferentes, estamos pavimentando um caminho muito mais seguro para que no futuro possamos alterar aquela implementação ou até adicionar novas funcionalidades com muito mais confiança, qualidade e rapidez.

Em resumo, qualquer que seja o objetivo: desenvolver uma nova funcionalidade incrível ou tornar uma implementação que já funciona num código legível, de fácil manutenção e que reflete claramente as decisões do negócio, uma cobertura eficiente de testes é um aliado valiosíssimo que nos permite pensar no que realmente importa: entregas de valor e um produto de qualidade.

Pirâmide x Troféu

Quando falamos de testes automatizados temos um conceito muito conhecido: o da pirâmide de testes, nele algumas camadas diferentes de testes são comparadas por características como custo, tempo de desenvolvimento e nível de integração, através dessa metáfora com a pirâmide podemos visualizar a quantidade de testes de cada de cada uma das camadas que deveríamos ter em nossas aplicações. A ideia é que testes unitários, mais simples e menos onerosos estejam na base, assim teríamos uma maior quantidade deles. Na sequência teríamos testes de integração que possuem um custo médio e por fim testes de ponta a ponta ocupariam o topo da pirâmide como o tipo de maior custo e também o de menor quantidade.

Pirâmide de testes
Pirâmide de testes

Puxando para o front-end temos um conceito que se assemelha à pirâmide de testes na metáfora visual mas difere na divisão de camadas, esse conceito é o troféu de testes, pensado para se adequar às particularidades do ecossistema javascript essa classificação propõe que a maioria dos nossos testes sejam de integração e também adiciona uma nova camada: a de testes estáticos.

A ideia aqui é pensar na relação entre o tempo investido e retorno gerado através de confiança. Essa classificação se baseia no conceito chave de que quanto mais o teste se assemelha a forma com que o software é usado mais confiança ele gera.

Troféu de testes

Essa forma de pensar pode ser um guia interessante para avaliar aonde focar nossos esforços na escrita de testes. Esse assunto é muito vasto e vale a pena ser explorado para os mais diversos contextos de desenvolvimento, inclusive esse cenário pode mudar, por exemplo, se estivermos falando de micro-serviços. Você pode ler mais sobre o modelo de pirâmide aqui e sobre a ideia por trás do modelo de troféu aqui.

Testes no desenvolvimento front-end

Cada uma das diferentes camadas de teste pode nos ajudar de formas distintas e complementares no dia a dia de desenvolvimento. Vale destacar algumas aqui:

Testes estáticos

Capazes de avaliar o código em tempo de desenvolvimento (sem necessidade de qualquer tipo de compilação), esse tipo de teste garante a checagem de tipos e o cumprimento de regras definidas pelo time de engenharia.

Não à toa esse tipo de teste encontra-se na base do modelo de troféu. Além de poupar tempo do desenvolvedor, testes estáticos podem gerar discussões valiosas sobre boas práticas a serem seguidas para garantir a qualidade do software.

No ecossistema javascript, diferente de outras linguagens, é necessária uma configuração no projeto para que essa camada funcione e algumas ferramentas podem nos ajudar com isso, o eslint é muito utilizado no mercado para a análise do código em tempo de desenvolvimento, ele notifica sobre possíveis erros de implementação e também é capaz de corrigir alguns deles automaticamente, tudo isso de forma customizada podendo atender as necessidades e acordos de cada projeto.

Outro grande aliado nos testes estáticos é o typescript, uma linguagem baseada em javascript capaz de checar os tipos em nossas implementações, garantindo maior confiabilidade e qualidade no código.

Tanto o eslint quanto o typescript possuem uma integração excelente com a maioria dos editores de texto e trazem uma experiência de desenvolvimento muito ágil e confiável.

Testes Unitários

Responsáveis por testar individualmente pequenas porções de código, em muitos casos o teste unitário busca validar uma função “pura” (que recebe uma entrada, processa os dados e devolve uma saída sem alterar o estado do contexto em que está inserida) nos diferentes casos a que ela pode ser utilizada verificando se o resultado é realmente o esperado. Nesse sentido os testes unitários se fazem fundamentais para garantir que as implementações essenciais para o funcionamento do nosso software tenham o comportamento desejado.

No conceito da pirâmide os testes unitários ocupam a base, e isso é muito justificável já que possuem o menor custo e garantem o funcionamento de unidades isoladas e independentes do código. Esse tipo de teste ainda possui muita importância no modelo de troféu, mesmo com o pensamento de reproduzir da maneira mais fiel possível o comportamento do usuário sempre teremos a necessidade de garantir que partes isoladas do nosso sistema se comportem como esperamos.

Testes de Integração

Além de verificar se nossas implementações se comportam conforme o esperado de maneira isolada, precisamos garantir que várias unidades funcionam juntas como deveriam, e esse é o objetivo de testes de integração. Com eles podemos simular desde a renderização de componentes na tela, passando por interações do usuário até finalmente checar se a aplicação tem o comportamento esperado a cada interação. Com isso, podemos ter visibilidade se os fluxos que estamos desenvolvendo funcionam como esperamos e até se uma nova implementação afeta de alguma forma algum fluxo já existente na nossa aplicação.

A possibilidade de renderizar um conjunto de componentes formando um fluxo completo, simular as interações do usuário e verificar se a aplicação se comporta corretamente fazem com que os testes de integração ocupem a maior camada no troféu de testes, com as ferramentas de desenvolvimento modernas conseguimos testar casos de uso muito completos sem ter um alto custo no tempo de desenvolvimento, dessa forma é possível equilibrar muito bem a confiança gerada por esses testes com o investimento necessário para desenvolvê-los.

Testes Funcionais

Os testes funcionais, que também podem aparecer como testes de UI ou testes end to end, são uma camada responsável por testar fluxos reais do software, ou seja para a realização desse tipo de teste é necessário ter um ambiente completo com a aplicação e todas as suas dependências rodando.

Geralmente as ferramentas de testes funcionais utilizam um navegador real para executar suas rotinas. Os testes dessa camada são muito confiáveis, em contrapartida é necessário um alto investimento de tempo e recursos, precisamos de um ambiente real configurado e também de um gerenciamento de dados que nos permita executar os testes sem impacto para o produto. Devido a esse alto custo, nos dois conceitos (pirâmide e troféu) esse tipo de teste aparece na menor porção.

Testes como documentação

Além da função de validação, testes também podem assumir um papel valioso de documentação. Como os testes basicamente apresentam casos de uso que verificam o funcionamento de diferentes cenários, muitas vezes é possível saber do que se trata determinada implementação sem percorrer várias camadas do código fonte em busca de respostas, um teste suficientemente legível pode poupar muito tempo no entendimento de alguma regra de negócio ou implementação específica.

Uma forma interessante de implementar testes é utilizando a técnica do TDD (Testing Driven Development) na qual a codificação se inicia baseada em testes, esse é um assunto que valeria um post (ou uma série de posts) mas essa técnica traz tantos benefícios que vale citá-la por aqui, ela segue alguns princípios básicos e importantes que nos garantem que nossos testes funcionarão como documentação e não só isso, ajudam a escrever um código mais testável e melhoram a experiência de desenvolvimento.

Dica: Nessa palestra do Robert C Martin — autor do livro Código Limpo — temos uma seção interessante sobre TDD.

Cobertura e qualidade de testes

Junto com a cultura de testes é natural que surjam perguntas sobre como medir a eficiência dos nossos esforços em testar nossa aplicação, o pensamento mais óbvio nesse sentido é medir a cobertura desses testes. Temos ferramentas no mercado que conseguem verificar a porcentagem do código que está sendo submetido a testes, mas aí vem um ponto importante: essas ferramentas conseguem identificar se determinado trecho de código está sendo verificado e até se uma condição específica está sendo avaliada, mas não avaliam como esse trecho está sendo testado. Tendo isso em vista, a preocupação maior não deveria ser em ter 100% do código testado, pois não necessariamente esses testes refletem da melhor forma os cenários que realmente deveríamos validar. É lógico que métricas como essa podem ajudar a medir a evolução da sua cobertura de testes, como por exemplo: um salto de 30% para 80% com certeza representa uma vitória. Mas o ponto principal não deveria ser atender completamente determinada ferramenta geradora de métricas e sim garantir que nossos testes conseguem prever com confiança os diferentes cenários e comportamentos esperados.

Indo além

São várias as vantagens que uma cultura de testes pode proporcionar para desenvolvedores front-end e para times de engenharia em geral, mas essa é uma provocação inicial, vários desses pontos poderiam ser discutidos individualmente de maneira mais profunda e detalhada. Entender a importância de uma cultura de testes para o produto e os benefícios que ela traz para a experiência de desenvolvimento é um passo inicial na direção de softwares construídos com mais qualidade.

Se você se gostou desse post e se interessa pelo assunto de qualidade de software e boas práticas de escrita de código pode conferir esse post do Luis Junior que fala sobre Código Limpo e tem tudo a ver com qualidade de software.

Ficou interessado pelo assunto? Deixe um comentário com críticas, elogios ou sugestões e até uma próxima.

--

--