Testes em aplicações React com VITE + VITEST
Tags: vite, vitest, react
Published: 2022-12-09 11:27:28

## Por que testar aplicações?

Não é raro encontrar em uma equipe, ou projeto, uma certa resistência no uso de testes de software. Seja por diversos motivos, desde a ideia de que o cliente não irá **ver** de fato o seu resultado, prazo de entrega apertado ou falta de conhecimento do time.

Independente do motivo que você não trabalhou com testes até agora, por mais que pareça um trabalho redundante em um primeiro momento, garanto que a longo prazo evitará muitos problemas.

Com um conjunto de testes bem estipulados, a confiabilidade da equipe aumenta. Ajuda o time a não ter medo de quebrar o sistema na hora de **refatorar** ou criar novas **features**. Diminui a necessidade do **QA** testar manualmente alguns processos. 

## Os tipos de testes

Antes de iniciarmos esse projeto, é importante termos em mente quais os tipos de testes existentes:

**1. Testes Estáticos:** identifica erros de types e sintaxe do código, como o **ESlint** e o **TSlint**.
**2. Testes Unitários:** testa um pedaço do código, geralmente um componente de forma isolada.
**3. Testes de Integração:** testa a interação de alguns componentes para se certificar de que deveriam funcionar corretamente, como por exemplo em um formulário.
**4. Testes End to End (E2E):** Simula o fluxo de um usuário dentro do sistema do início ao fim, como por exemplo em um e-commerce, desde a escolha de um produto até o pedido final.

Nesse artigo iremos cobrir os testes unitários e te integração, ok?

## Por que usar o VITE + VITEST?

O **VITE** é uma ferramenta de build rápida de compilar projetos react. Aliado ao **VITEST**, que contém uma estrutura de testes integradas ao VITE, diminui a complexidade de configurações.

## Let's Start!

**Link do projeto:** [https://github.com/dnokaneda/vitest-examples](vitest-examples)

Primeiro precisamos criar o nosso projeto, certo? Para isso, rode o comando:
```
yarn create vite
```
Na sequencia, digite um nome de projeto da sua preferência (no nosso caso _vitest-examples_), selecione o framework **react** e a variante **typescript**.

A instalação será iniciada e uma pasta será criada com o nome do projeto. Acesse-a pelo **prompt de comando** e rode o comando abaixo:
```
yarn dev
```
Em seu navegador você verá o seguinte resultado:

![vite+react](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oxj9gelduxocv3i8lwyy.jpg)

## Alterar o tema p/ dark

Nesse primeiro momento, vamos somente alterar o arquivo **index.css** para o **tema dark**, que deixará a tela um pouco mais agradável. 

![index.css](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ywc6im4sef070v9n0iky.jpg)

Não se preocupe na estilização agora, afinal, o objetivo aqui é realizar diversos exemplos de testes na plataforma.

## Instalação de dependências

O **VITEST** é nativo do **VITE**, simplificando muito a parte de configuração. Evita que você utilize o babel ou configurações específicas ao utilizar outras libs de testes.

Vamos instalar todas as dependências que precisamos para iniciarmos os nossos testes. Segue o comando:
```
yarn add -D vitest @testing-library/react @testing-library/jest-dom jsdom
```
Além do **VITEST**, utilizaremos a biblioteca do **testing-libray**, que contém diversas ferramentas de testes com compatibilidade para diversos frameworks, inclusive o react.

O **JSDOM** também será instalado justamente para fazer as vezes do browser. Os testes serão rodados via prompt de comando, portanto, o responsável por "simular" o projeto e interagir com ele será o **JSDOM**. 

## vite.config.ts

Após a instalação, vamos configurar de fato o ambiente de testes. Para isso, acesse o arquivo **vite.config.ts** e adicione os itens marcados na imagem abaixo:

![vite.config.ts](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4o5a3qntshqt8i92fxce.jpg)

## package.json

Agora precisamos criar o comando test no arquivo **package.json** para rodarmos os testes de nossa aplicação.

![package.json](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/41kbofzheo2u44b021a2.jpg)

## O primeiro teste

Vamos começar com um teste simples: nos certificar que a aplicação está rodando. Para isso, criaremos um arquivo chamado **App.test.tsx** dentro da pasta src. 

Segue o código que deveremos escrever:
![App.test.tsx](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tl4l8szdrmgt3b59exiv.jpg)

Agora vamos à algumas explicações. Na _linha 3_ estamos importando 3 funções principais do **vitest**: 

- **describe:** é uma função que define um contexto para um grupo de testes que serão executados. Normalmente é criado um contexto para cada componente ou tela que será testado no projeto.

- **test:** é a função de teste em si. Recebe um nome como parâmetro e define um conjunto de expectativas ao funcionamento do componente a ser testado.

- **expect:** é a função que verifica a hipótese a ser testada do componente. Foi renderizado com sucesso? Existe um texto específico na tela?

Para executarmos nosso primeiro teste será preciso rodar o seguinte comando:
```
yarn test App.test.tsx
```
O resultado será esse:
![app.test.tsx](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hi161xd6a80sr8jgkqvx.jpg)

## Afinal, o que aconteceu aqui?

Na _linha 9_ utilizamos o método **render** do **testing-library**, que irá "_renderizar na memória_" a página App, com o auxílio do JSDOM, para que testemos uma hipótese (_linha 10_): Existe um texto _"Vite + React"_ no documento? 

O método **render** possui um conjunto de funções que nos ajudarão a testar diversos aspectos do projeto, seja encontrar um texto, uma tag, uma classe css ou estilo. Veremos alguns exemplos adiante.

## Componente button

Em um projeto **react** é comum termos diversos componentes com funções bem específicas, e é aqui que os testes unitários fazem bastante sentido. Queremos ter a certeza que cada componente está funcionando corretamente antes de subirmos para produção.

Um botão será um exemplo perfeito para esse cenário. 
Segue o código de um botão básico em react:
![Button.tsx](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p2xs097ivjwkljc9jmor.jpg)

## Teste unitário: componente button

Aqui iremos testar duas funções básicas do botão: renderizar na tela e disparar o evento de clique. Claro, há outras possibilidades, mas vamos começar devagar.

Um novo arquivo chamado **Button.test.tsx** será criado:
![button.test.tsx](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ozh1zf9e7iz43a43owlb.jpg)

Entendendo o teste acima:

- **Linha 7:** cria um conjunto de testes com o nome de "Button test";
- **Linha 8:** função de teste de renderização do componente;
- **Linha 13:** função de teste do disparo do evento de clique;
- **Linha 14:** função genérica do **VITEST** que verificará se foi disparado ou não após o clique do botão;
- **Linha 16:** _getByTestId_ é a função que será usada para encontrar um elemento com testId específico no documento renderizado;
- **Linha 20:** O método _fireEvent_ irá disparar a função click no elemento com o textId _"component-button"_.
- **Linha 21:** O _expect_ verificará se a função _"handleClick"_ foi executada 1 única vez (_toHaveBeenCalledTimes_);

Novamente, segue o comando:
```
yarn test Button.test.tsx
```
O resultado será esse:
![button.test.tsx](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jps0xx9qqayijugy2e9q.jpg)

## Teste de Integração: App.tsx

Agora que já descobrimos que o botão está funcionando corretamente (de forma isolada, muito importante entendermos isso), nossa próxima etapa é testá-lo na página **App.tsx**. 

Mas antes, precisaremos fazer alguns ajustes no projeto.

## App.tsx

Como citado anteriormente, estamos aproveitando o código gerado pelo **VITE**, portanto não iremos perder tempo criando estilos próprios nesse momento. A única inclusão é na _linha 44_, ao adicionar uma margem à direita.

![index.css](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/907gg0m64w608fvymgch.jpg)

O motivo da margem? Vamos criar dois botões (_linhas 24 e 24_), uma para somar e outro para subtrair o valor de um contador. Eles ficarão alinhados horizontalmente na tela.

![app.tsx](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oiftz224z7uk3en41u17.jpg)

O código ficou assim:
![app.tsx](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ho3qa6eg0ourqbrogjos.jpg)

## O Problema

Existe um detalhe no código acima que queria explorar com vocês. Reparem que no arquivo **App.tsx** existem dois botões (_linha 24 e 25_). Cada um com uma função específica. 

Se formos criar um teste de integração para testar a soma, por exemplo, o **VITEST** não conseguirá identificar o botão exato a ser usado, uma vez que ambos estarão com o mesmo **data-testId**. 

## Button.tsx

A solução é bem simples: passar o **dataTestId** por parâmetro para o componente (_linha 8_).

![button.tsx](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fs8bb2l9mzgvleyljmp7.jpg)

Dessa forma, caso tenhamos um valor no parâmetro **dataTestId** ele será utilizado, do contrário, será usado o **data-testId** padrão (_"component-button"_).

# App.tsx

Vamos atualizar o código do arquivo **App.tsx** com os ids específicos de cada botão (_linhas 27 e 33_). Dessa forma conseguiremos disparar um evento de clique em cada botão na hora dos testes.

Além disso, será necessário incluir um data-testid no valor do contador para monitorar se o teste foi realizado com sucesso (_linha 22_).

![app.tsx](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0k3wpuq8zhbuur4lbfis.jpg)

## Testes de Integração: finalmente

Agora sim! Com todos os ajustes feitos, podemos criar o nosso teste de integração. 

No arquivo **App.test.tsx** iremos criar dois novos testes, um para o botão soma (_linha 13_) e outro para a subtração (_linha 20_).

![app.test.tsx](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v48qi2w0hem2uod5fm5w.jpg)

Entendendo o teste acima:

- **Linha 13:** Renderiza a página App. Além disso, através da desestruturação da função render, obtemos o método **getByTestId**, que será usado para encontrar o botão específico do qual queremos testar;
- **Linha 16:** Dispara o evento de clique no botão de soma. Só conseguimos identificar o botão exato, pois criamos um **dataTestId** próprio para cada botão na tela;
- **Linha 17:** Testa a condição proposta: Ao clicar o botão de soma, espera-se que o valor vá de 0 para 1. Usando o método **getByTestId("total-cliques")** podemos ler o conteúdo exato do contador.;

Ao rodar o teste, temos o seguinte resultado:
```
yarn test App.test.tsx
```
![app.test.tsx](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/62srcizbyq8u4ddib3i6.jpg)

## Refactore App.test.tsx

Perceba que criamos dois testes muito parecidos, um para o botão soma e outro para a subtração. Podemos uni-los em um único teste, conforme o exemplo abaixo:

![app.test.tsx](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zoa06g630e8ewoih1qlw.jpg)

**Mas qual a vantagem?** Bom, há um pequeno ganho no desempenho, pois a renderização da página é feita uma única vez para o testes dos botões, mas perde-se o rastreio de qual botão em si falhou, uma vez que os dois botões precisam funcionar para o teste ser bem sucedido. 

Fica ao seu critério. Depende da complexidade da tela ou das funções de cada botão. Por isso é importante pensar exatamente o que cada teste vai realizar, garantindo assim a qualidade do seu código.

## Testes de classes e estilos

Em projetos grandes, com uma equipe de **UX/UI** comprometidas a criar um **design-system** da aplicação, o front-end precisa garantir que os padrões visuais estipulados pelo time sejam atendidos.

## index.css

**Vamos criar um cenário:** O botão primário e secundário possuem estilos próprios. Caso mude de programador, o teste irá garantir que o componente mantém o padrão visual do projeto. Se a cor do botão é azul, é azul! Certo?

![index.css](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1mp25oasv6kn0ew8b5zn.jpg)

No componente **Button.tsx**, vamos criar um novo parâmetro: secondary (_linha 6 e 9_). Em **className** criamos uma condição para verificar se o botão é primário (padrão) ou secundário (_linha 12_).

![button.tsx](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ngjtwfqan7zxokukafh2.jpg)

Vamos agora colocar o botão de subtrair como secundário. 

![app.tsx](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fu2cys546pqz6smq7c30.jpg)

## Testes unitários: Estilos e Classes

Para garantir o **style-guide** do projeto, vamos criar os testes abaixo no arquivo **Button.test.tsx**:
![button.test.tsx](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xwrrod0w2z1xpx3k382x.jpg)

**Foram criados quatro novos testes**: dois para o botão primário e dois para o secundário, sendo um deles um teste de classes e outro um teste de estilos.

Como exemplo, vamos entender o teste primário:

- **Linha 28:** na função **expect**, utlizamos o método **toHaveClass** para identificar se a classe _"button-primary"_ está sendo usada no componente. O parâmetro **exact** garante que a única classe a ser usada no botão é a _"button-primary"_. Se preferir usar várias classes no componente e queira testar se a _"button-primary"_ esteja entre elas, basta colocar o parâmetro **exact** como **false**.
- **Linha 36:** O método **toHaveStyle** verificará se o estilo a ser testado (_"backgroundColor"_) contém o valor correto (_"#7dd3fc"_).

![button.test.tsx](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vaqtokbciz4yn948ejlz.jpg)

## Conclusão

Nesse artigo foram demonstrados alguns testes básicos que podemos utilizar em nossos projetos de **front-end**. Testamos componentes e funções básicas. Aplicamos alguns conceitos de testes unitários e de integração, o que já é um grande começo. O real poder dos testes automatizados é no fluxo do sistema, simulando comportamentos do usuário e identificando possíveis erros. 

Espero que esse artigo lhe incentive a iniciar o programa de testes de suas aplicações. Adoraria ler suas considerações e caso tenha alguma sugestão/melhoria, por favor, compartilhe comigo.

Grande abraço!