entender testes no frontend

douglasabnovato
9 min readMar 6, 2023

Para elevar as habilidades de desenvolvedor, adicionar testes na rotina de criar suas aplicações é um passo importante para o seu nível de soluções. Nesse texto, vou pesquisar tipos de testes para desenvolver e como podemos fazer.

Queremos ter feedback instantâneo sobre as mudanças que fazemos e ter confiança que o código funciona como planejado, sem precisar esperar ter uma aplicação pronta que possa ser validada.

Para isso ser possível, temos diferentes tipos de testes automatizados que podemos implementar na nossa aplicação. Com o Desenvolvimento Orientado a Testes, podemos até mesmo escrever o teste antes de implementar o código, definindo o que esperamos que aquele pedaço de código faça antes que ele exista.

No Desenvolvimento Ágil, testar é parte da nossa rotina como pessoas desenvolvedoras. É tão importante que, há muito tempo atrás, Mike Cohn introduziu o conceito da Pirâmide de Testes, buscando apresentar uma forma de como pensar a estratégia de testes.

Na Pirâmide de Testes, vemos mais testes unitários na base, por serem mais rápidos de desenvolver e rodar. A quantidade de testes diminui conforme vamos subindo na pirâmide, pois aumenta o esforço necessário e o tempo de execução de cada tipo de teste.

Para fazer um teste de UI, por exemplo, é necessário muito mais esforço e demora mais para rodar e ter feedback. Por isso, segundo a pirâmide, devemos ter menos testes deste tipo.

testes que podemos usar no front-end (❤️)

Testes Unitários

Podemos considerar uma unidade como sendo a menor parte de um código. Pode ser uma função, por exemplo. Quando falamos de testes unitários, estamos verificando que essa pequena parte de código faz o que deveria fazer, independente de outras unidades.

Esse tipo de teste é rápido e fácil de escrever e, por isso, é recomendado escrever muitos destes. Quanto mais partes do código conseguirmos testar com esse tipo de teste, menos vamos precisar fazer os outros tipos de testes, que são mais custosos.

Quando desenvolvemos software para o front-end, muitas vezes trabalhamos com componentes. Os testes unitários nos permitem garantir que eles funcionam como esperado de forma isolada, mas também é preciso saber se esses componentes funcionam quando utilizados de forma conjunta.

Podemos alterar as propriedades que um componente espera receber e alterar seus testes unitários, garantindo que continua funcionando de forma isolada.

Os testes unitários vão continuar passando. Porém, como saber que os outros componentes que o utilizam não vão parar de funcionar com essa mudança? Para isso, podemos precisar de outros tipos de testes.

Algumas Ferramentas para teste unitário: Jest, Jasmine, Mocha e Chai

Testes de Integração

Nós já sabemos que as unidades do nosso código estão funcionando corretamente de forma isolada, mas precisamos garantir que, quando uma parte se comunicar com a outra, as coisas vão funcionar como esperado.

No front-end, acredito que os testes de integração são ainda mais importantes, porque queremos nos certificar de que nossos componentes funcionam conforme esperado quando estão sendo usados em conjunto.

Por exemplo, se tivermos um formulário contendo vários inputs diferentes e com um botão para enviar os dados. Queremos testar que, ao preencher todos os dados corretamente e clicar no botão, veremos a mensagem de sucesso esperada. Sendo cada input e botão um componente separado, não seria suficiente testar cada um isoladamente.

Algumas Ferramentas para teste de integração: Jest e Cypress

Testes E2E

Os testes end-to-end vão ainda além, testando os componentes trabalhando de forma integrada, mas pensando na jornada do usuário.

Podemos utilizar ferramentas que automatizam ações que seriam realizadas pelo usuário em um contexto real. A aplicação é iniciada no browser, que navega para a nossa página e executa as ações que forem definidas, como clicando em botões, preenchendo campos e verificando se os resultados são conforme esperado.

É necessário investir mais esforço para escrever esses testes e eles demoram mais para rodar, porém é o tipo de teste que nos traz mais confiança, pois sabemos que a aplicação está funcionando exatamente como esperado para o fluxo que será utilizado pelo usuário.

É importante ter cuidado para não testar a mesma coisa em testes diferentes, tornando esse tipo de teste ainda mais custoso.

Algumas Ferramentas para teste E2E: Cypress, TestCafe e Puppeteer

Testes de Snapshot

Focado na interface, a ideia é ter certeza que, quando fizermos quaisquer alterações, ela não terá mudanças inesperadas.

Quando um teste de snapshot é criado, ele renderiza o componente, ou seja, transforma em algo que o navegador consegue entender e mostrar na tela. Em seguida, “tira uma foto” do que foi renderizado, e guarda aquela imagem.

Cada vez que os testes forem rodados, o que for renderizado é comparado com a imagem que estava guardada. Se houver alguma diferença, o teste falha e sabemos que algo na nossa interface foi alterado, sem precisar rodar a aplicação inteira pra isso. Se fizemos uma alteração intencional na interface, basta atualizarmos a imagem daquele teste.

Esse tipo de teste não substitui os outros, pois tem objetivos diferentes e devem ser usados em conjunto. Os testes de snapshot ajudam a ver exatamente o que foi alterado e de uma forma bem simples.

Algumas Ferramentas para teste de snapshot: Jest

Testes de Acessibilidade

Quando escrevemos software para o front-end, é muito importante manter em mente a acessibilidade.

Queremos remover barreiras que possam impedir pessoas de utilizar nossa aplicação. Existem uma série de padrões de como desenvolver software que é acessível para todas.

Nós podemos ter testes automatizados que verificam esse aspecto do nosso código. Existem diversas ferramentas que nos permitem fazer isso, verificando se não temos nenhuma violação das diretrizes de acessibilidade na web.

Algumas Ferramentas para teste de acessibilidade: Jest Axe e Pa11y

Verificação de Tipos Estáticos

Por mais que não seja exatamente um tipo de teste, a verificação de tipos estáticos pode ser muito útil quando trabalhamos com linguagens como Javascript.

Utilizando ferramentas que trazem essa funcionalidade, podemos identificar problemas e possíveis erros antes mesmo de executar o código.

Esse tipo de verificação pode tornar nosso processo de desenvolvimento mais produtivo e trazer mais confiança sobre o código.

Algumas Ferramentas: Flow e TypeScript

Existem muitos outros tipos de testes que podemos utilizar no front-end. Escolher quais tipos vamos utilizar depende do contexto do nosso projeto e do que for decidido com a equipe.

O que sabemos é que a abordagem tradicional nem sempre é válida aqui, sendo necessário adaptar os tipos e a quantidade de cada tipo para o que faz sentido no nosso contexto.

Escreva código fácil de testar

Quando trabalhamos com um código em que há muitas dependências e em que as responsabilidades não estão bem definidas, escrever testes pode se tornar uma tarefa chata e difícil. Por isso, é importante ter em mente que queremos escrever código que seja fácil de testar.

Podemos fazer isso tendo em mente alguns princípios quando estamos desenvolvendo, como manter uma única responsabilidade para cada pedaço de código, uma função deve fazer apenas uma coisa, por exemplo, buscar ter menos dependências entre diferentes partes do código e, ainda mais relevante para o front-end, manter a lógica de negócio separada da interface da aplicação.

Escrever código fácil de testar também ajuda a tornar o código mais legível, fácil de dar manutenção e de implementar funcionalidades novas.

Outra forma de fazer isso é ter o desenvolvimento sendo guiado por testes, escrevendo o teste antes mesmo de implementar o código.

TDD — Test Driven Development

É o que conhecemos por TDD e que também pode ser utilizado no front-end. Dessa forma, pensamos no comportamento que estamos esperando para aquela funcionalidade e escrevemos um teste para isso. O teste irá falhar, pois a funcionalidade ainda não existe. Logo após, implementamos o código e rodamos o teste novamente, que agora irá passar. Seguimos nesse fluxo também para fazer refatorações, garantindo que aquele pedaço de código faz somente o que é esperado dele e que está funcionando corretamente.

Legal, mas por onde eu começo?

Implementar uma aplicação com uma estratégia de testes bem pensada é ótimo na teoria, mas na prática sabemos que é muito mais complicado. Diversas vezes grande parte da aplicação já foi escrita e com quase nenhum teste.

Como saber onde começar a testar as funcionalidades que já existem?

Acredito que faz mais sentido começar pelo fim. Podemos utilizar os testes E2E para garantir que a aplicação funciona conforme esperado pelo usuário, testando de forma automatizada os principais fluxos e garantindo que eles não irão quebrar quando fizermos alterações ou incluirmos novas funcionalidades.

Depois, podemos começar a inserir os outros tipos de testes no nosso processo de desenvolvimento e aos poucos ir “equilibrando”.

Independente da estratégia de testes que decidirmos seguir, o importante é começar uma conversa e estimular a cultura de testes, tendo em mente que todo o time é responsável pela qualidade.

um dev frontend buscando entender mais de testes durante a fase de desenvolvimento para a sua aplicação

Uma das principais vantagens de entregar um código testado é a confiança que o teste nos dá e aquela sensação ímpar de entregar um código que não só atende os números de code coverage.

A proposta é testar além da lógica do negócio com os testes unitários, testar também as saídas que deveriam estar na tela e simular as interações do usuário com nossa aplicação. Uma frase famosa diz:

Quanto mais seus testes se assemelham com a forma de uso do seu software, mais confiança eles podem lhe dar — Kent C. Dodds.

A documentação oficial de testes do React Native segue a mesma linha dessa mensagem e recomenda que devemos testar o comportamento do usuário e o que ele esperava ver ou ouvir como saída.

Uma das bibliotecas de teste que o React e o React Native sugerem é a Testing Library que na verdade é uma “família” de bibliotecas de testes para vários frameworks e libs como React, React Native, Angular, Vue, Svelte e até para o DOM puro.

Essa biblioteca tem o mesmo entendimento sobre os testes de comportamento do usuário e adicionando que não devemos nos atentar aos detalhes de implementação de um componente.

A documentação da Testing Library diz que não devemos nos preocupar em testar o estado interno de um componente e ele nem nos dá essa possibilidade. Para efeito de comparação com a biblioteca Enzyme é possível fazer isso.

A Testing Library diz que sempre devemos testar o que o usuário pode ver na tela ou ouvir utilizando as tags de acessibilidade e aí que entra o Novo Frontend.

A mentalidade de criar o frontend com foco no usuário, o usuário no centro da aplicação, no que vai ver ou ouvir, como vai interagir, como será a sua jornada.

Voltando a falar exclusivamente do React e da famosa (e temida) pirâmide de testes, podemos dividir os teste em:

  • Análise estática de código: Utilizamos TypeScript e ESLint para tal.
  • Testes de unidade/unitários: Testamos funções isoladas que sozinhas geralmente não têm efeito algum.
  • Testes de integração: Testamos a integração dessas funções isoladas, porém agora no contexto onde elas deveriam ser aplicadas se integrando a outras funções que dependem de seu retorno.
  • Testes E2E: Testamos fluxos que o usuário realizaria de ponta a ponta sem simulações.

Temos também o teste de componente, uma vez que tudo no React é um componente. Um Button é um componente assim como um Menu é um componente que pode ter componentes de Text, Button e outros dentro dele.

Desse modo, um teste de componente que é a base do React, flutua entre testes unitários e testes integrados. Em todos esses testes de componente podemos ter dois “subtipos” de teste:

  • Teste de comportamento
  • Teste de renderização (snapshot)

Uma representação gráfica dessa ideia baseada na documentação oficial de testes:

Nos testes de comportamento simulamos o comportamento do usuário, se temos um componente do tipo Button geralmente vamos ter associado uma função onClick ou onPress e devemos garantir que essa função foi chamada. Esse seria um tipo de teste unitário para esse Button.

Já no teste de renderização avaliamos se o botão foi renderizado da forma que deveria ser e para isso podemos utilizar os testes de snapshot ou garantir que a propriedade está presente.

Por exemplo:

  • Estilo: Botão desativado tem tal cor e/ou a propriedade disabled
  • Texto: Botão em estado de loading deve estar escrito “loading”

Para escrever esse texto, li os links a seguir.

A ideia desse artigo é esclarecer minhas dúvidas iniciais de quais testes implementar nos projetos que tenho no meu github. Tenho projetos frontend e backend, simular a tomada de decisão de quais testes implementar e documentar os motivos que me levaram a montar a estratégia.

Espero que tenha gostado. Deixe o seu aplauso e até o próximo texto.

--

--