Testes Eficientes Usando React Testing Library

Ricardo Pedroni
RPEDRONI
Published in
7 min readFeb 17, 2021

Testando e tornando seus componentes React indestrutíveis, da forma certa.

A partir do momento que o React ganhou popularidade, muitas pessoas já pensavam na melhor forma de poder testar componentes para garantir que os mesmos fizessem o que era esperado. A ideia é simples: fazer testes que garantem que aquele botão faça algo quando apertado, que o seu <Dropdown /> mostre uma lista de opções quando acionado, e assim vai.

O que não foi tão simples nessa história toda foi chegar num consenso de qual é a melhor forma de testar isso. Afinal, se tem uma coisa que engenheiros, cientistas da computação e profissionais em tecnologia em geral fazem bem, é saber se quebrar na porrada proverbial brigando por um ponto de vista.

Engenheiros definindo os novos features do JavaScript/ES 2021

A discussão era em torno exatamente como testar os componentes. Uma das bibliotecas a ganhar muita atenção logo no começo foi a Enzyme. Criada pelo Airbnb, a premissa do Enzyme é facilitar o acesso a valores de um componente renderizado, podendo verificar e testar esses valores para ver se batem com os valores esperados pelo desenvolvedor.

No teste acima, usamos o shallow do Enzyme para renderizar (em memória) o componente e, usando um helper .state(), pegamos um valor de dentro do estado do componente e testamos se o valor equivale ao que esperamos, nesse caso, se é true

Testes Testando Errado?

O que acabamos de ver é um teste, não é? Formalmente, com certeza. Mas a discussão que gera certa controvérsia é que um teste desse tipo é um teste que testa o componente de forma errada. A razão disso é que veja bem o que está sendo testado — nesse caso, o state do componente (que é um valor interno e privado do componente) averiguando se o estado do componente tem um certo valor. O problema disso (e eu concordo plenamente) é que testar valores desse tipo testam muito mais detalhes de implementação do que o funcionamento correto do componente.

Se eu testo que um componente começa com um valor interno em true e que, ao ser clicado, passa esse valor para false, eu estou efetivamente estando o componente? Ou seja, estamos garantindo que o componente FAZ o que ele precisa fazer ou estamos testando se o desenvolvedor programou corretamente o componente? Nesse tipo de teste, acaba sendo o último — o teste passa a ser uma verificação de programação invés de testar o que efetivamente faz.

Teste o que um componente faz e não como ele foi programado

Com esse conceito em mente, o React Testing Library (RTL) foi criado. Inventado pelo nobre Kent Dodds, o RTL força (na amizade, de forma delicada) você a escrever testes que verificam o uso e a funcionalidade de um componente, imitando justamente como um usuário interfacearia com o mesmo. Dessa forma, o teste testa certo 👌

Testes Testando Certo!

Vamos criar nosso componente (imaginário) a ser testado, o <Dropdown />, usando nossa lista de requisitos abaixo:

  1. Começa fechado
  2. Ao ser clicado enquanto fechado, abre e mostra a lista de opções que recebe como parâmetro
  3. Quando uma opção é clicada, fecha a lista e indica a opção selecionada

Mesmo não sendo muito complexo, ter uma lista formal em mente do que você espera de algum pedaço de software e sempre útil. Com isso em mãos, vamos começar a ver como podemos testar e garantir que nosso componente faz o que esperamos que ele faça, criando os testes baseados nessas funcionalidades que destacamos (⚠️ Não vou explicar como instalar o RTL nesse post porque depende muito do ambiente em que está trabalhando — assista o vídeo no meu canal do YouTube para ver um exemplo funcional)

Repare que estou incluindo uma série de bibliotecas nos imports. Dessa lista, destacamos:

  • @testing-library/jest-dom: adiciona métodos de verificação ao Jest, úteis para testes de componentes, como o .toBeInTheDocument() e .toHaveValue();
  • @testing-library/dom: entre outras coisas, te fornece o screen, que é como um browser em memória, que mantém o componente renderizado e é de onde vamos puxar os componentes baseados nos queries do RTL;
  • @testing-library/react: adiciona a capacidade de renderizar componentes React no mundo do RTL;
  • @testing-library/user-event: fornece ações de usuário para interagir com os componentes renderizados.

Com toda essa parafernália instalada e incluída, podemos começar a escrever nossos testes, focando em cada um dos testes por vez:

Normalmente o primeiro passo do teste de componente é renderizar o componente para que RTL possa fazer um query em cima dele depois. Aqui estamos renderizando nosso Dropdown com algumas opções de escolha de Pokémon (Por que Pokémon? Poké não? 🥁), o título do dropdown quando ele estiver fechado e a função que vai ser chamada quando uma das opções é selecionada.

Repare que passamos uma função vazia para onSelect e é normal ver valores “vazios” assim em testes. Isso é feito para explicitar que aquela prop não será testada e seu valor não importa para o respectivo teste

O primeiro teste é para garantir que o Dropdown comece fechado, sem mostrar as suas opções. A primeira pergunta portanto a se fazer é a seguinte: como testamos que algo não é visível?

Uma das formas é verificar se um valor interno do Dropdown (e.g. um valor de estado isOpen) é false, garantindo que booleanamente temos o valor correto para essa variável. Mas isso implica no problema que levantamos antes: não teste implementação, teste funcionalidade. E a melhor forma de testar funcionalidade é imitar o que seu usuário vê e faz — e é justamente nesse ponto que o RTL brilha.

Imitar seu usuário nesse caso é verificar que algo não pode ser visto e o RTL (junto com o @testing-library/jest-dom ) fornece formas eficientes de testar isso:

Depois de renderizar o componente, usamos o screen , um utilitário que simula como se fosse a tela de um usuário, que fornece vários métodos para podermos buscar “coisas” dentro do que foi renderizado. Essas coisas podem ser inputs, labels, botões, checkboxes e qualquer outro tipo de elemento que o componente pode gerar. As opções para buscar esses elementos também são diversas e o RTL incentiva que a busca deve ser feita da forma mais “usuário” possível, ou seja, que se busque como um usuário buscaria aquele elemento.

No nosso exemplo, usamos o texto da lista de opções do Dropdown mesmo, que seria algo bem visual que nosso usuário viria. Isso também poderia ser por role (algo como screen.getByRole('button')) dependendo do nosso componente (a sequência de prioridade de busca pode ser vista aqui, junto com uma explicação). No nosso teste, foi verificado que nenhum elemento com o nome dos Pokémão que passamos estava presente no documento e que, se estivesse, falharia nosso teste. As opções de asserção são muitas, e uma lista gigante pode ser encontrada aqui.

Agora que você entendeu a lógica do que deve ser feito para fazer um teste da forma correta, pause esse post (tira o dedo desse scroll!) e elabore mentalmente como os próximos dois testes devem ser feitos. O que falta testar então é:

  1. Ao ser clicado enquanto fechado, abre e mostra a lista de opções que recebe como parâmetro
  2. Quando uma opção é clicada, fecha a lista e indica a opção selecionada

Pensa bem — não vai demorar mais que 2 minutos, beleza?

Pensou?

Maravilha?

Pensou bem?

Ótimo! (ou você mentiu mas aí vai na tua consciência)

Bora lá.

Mais Testes Testando Certo

Nosso segundo teste aborda a questão de uma ação do usuário e responder a essa ação. Para isso, temos a maravilhosa biblioteca do @testing-library/user-event , que simula lindamente a interação de um usuário com o browser. Alguns dos métodos que ela expõe são o click, o clear e o type , dentre outros, que podem ser vistos aqui. Então simbora para nosso teste!

Logo de cara, repare que passamos nossas opções e título para variáveis, facilitando o reuso deles e, mais importantemente, mostrando a relação explícita entre aquele valor e o valor testado. Sempre que puder mostrar que intencionalmente verificou um valor, faça isso.

Em seguida, renderizamos o componente e checamos novamente que nenhuma opção aparece (não tem problema repetir esse pedaço do teste porque faz sentido semântico ao teste atual também). Agora o getByRole vai servir para pegar uma referência ao elemento principal do Dropdown e vamos usar o userEvent para disparar uma clicada nesse botão (é um componente imaginário, então tô imaginando que o elemento principal é um botão 🤷‍♀️). Uma vez clicado, testamos para garantir que as opções do dropdown vão estar presentes na tela (por exemplo, esse getByTest poderia ser um screen.getByRole(‘menuitem’, { name: options[0] }) caso as opções fossem ítens de um menu). De qualquer maneira, garantimos que eles estão presentes e visíveis 👀

E para nosso último teste, nada especial — vamos agora garantir que “algo aconteça” e que somos sinalizados que esse “algo” aconteceu.

E voilá: mágica. Tudo começa igual mas repare agora que estamos passando uma função mockada para o onSelect, justamente porque queremos verificar se essa função vai ser chamada quando deve ser chamada. Renderizamos, abrimos e selecionamos uma opção (a melhor delas, Bulbasaur), e verificamos que o menu fechou depois de opção selecionada. Inclusive foi verificado que a função mockonSelect foi chamada para garantir que o componente sinaliza o componente-pai que uma opção foi selecionada. Você pode me questionar: “Ricardo, seu cusão, você não disse que testar implementação é errado?” e eu te respondo — Sim, mas nesse caso defendo por três razões: 1/ é uma funcionalidade essencial desse componente que, se não funcionar, inutiliza o componente 2/ não entra em muitos detalhes de implementação, apenas que “algo” acontece e 3/ Nunca leve regras muito a sério — se algo parece importante para você, vai lá e faça, caraio.

Conclusão

Agora que você sabe testar corretamente componentes, não quero ver mais aplicação quebrando por aí. Se você curtiu esse post, me faça o pequeno favor de se inscrever no meu canal do YouTube, onde tenho um vídeo abordando justamente esse assunto em detalhes ;)

TLDR;

  • React Testing Library (RTL) é uma biblioteca de teste para React que facilita e incentiva tests feitos da forma correta — teste seus componentes e como seu usuário os usaria;
  • Teste funcionalidade e não valores — imite o que o seu usuário faria e não o que você espera do desenvolvedor;
  • Utilize um test runner qualquer como o Jest e rode seus testes normalmente, junto com as capacidades adicionais de query e testes do RTL

Espero que esse post tenha te ajudado!
Ficou com dúvida ou quer mandar uma real? Deixa nos comentários!
Ah, e me acompanhe também no YouTube: https://bit.ly/3q0TIAU ✌!

--

--

Ricardo Pedroni
RPEDRONI

O Professor Ricardo Pedroni ensina conceitos importantes e boas práticas de desenvolvimento de projetos em software. YouTube https://bit.ly/3q0TIAU