Testando componentes React

Hudolf Hess
mercos-engineering
Published in
6 min readAug 31, 2017

Quando comecei a me aventurar no React, por não ter um background muito forte com o javascript moderno (digo moderno, porque quando comecei a trabalhar com web, o ES5 tinha acabado de sair do forno), tomei um certo dano para tentar aplicar os mesmos tipos de testes que faço diariamente no backend, principalmente quando se trata de TDD.

Depois de aprender um pouco mais sobre o React (e também Redux), comecei a estudar alguns frameworks de testes, o React já te dá algumas ferramentas para isso, mas na própria documentação recomenda-se o enzyme (http://airbnb.io/enzyme/).

Primeiros passos

Para os primeiros testes de componentes de tela, comecei a usar o enzyme, por ter uma documentação completa e uma API (ao meu ver) muito boa.

Para os exemplos neste post, utilizarei um projeto pessoal que consome a API da Marvel, você pode ver mais detalhes em: https://github.com/hudolfhess/MarvelHeroes (o projeto é para estudos, e deverá mudar com o tempo, mas os exemplos ainda continuarão aqui).

Serão dois exemplos de componentes de tela: SearchBar (que é apenas um input para fazer uma busca) e ListHeroes (que lista os heróis retornados da API).

Exemplo 1 — SearchBar.jsx

SearchBar.jsx — Como podemos ver, não houve TDD aqui :(

Bom, até o momento esse é o código final do componente SearchBar, onde ele basicamente exibe um input e recebe um método para controlar o onChange do input. Podemos notar também, que ele possui um controle para que o onChange seja chamado apenas após 300ms depois de parar de digitar, isso para evitar que muitas requests sejam efetuadas na API enquanto o usuário está escrevendo o termo a ser buscado.

Mas e agora? Como podemos testar esses comportamentos?

Bom, vamos listar o que queremos testar aqui:

  • O componente deve renderizar sem erros;
  • O valor da prop defaultValue deve ser iniciado no state do componente;
  • O valor do state search deve ser igual ao valor do input da busca;
  • Ao alterar o valor do input, deve ser disparado o evento passado pela prop onChange ;
  • Ao alterar várias vezes o valor do input, o evento passado pela prop onChange deve ser chamado apenas uma vez com o último valor passado.

Por enquanto essa é a nossa lista de testes. Vamos lá, mãos a obra.

Criando arquivo de testes

Você pode criar o arquivo dentro da mesma pasta do componente, eu prefiro deixar separado, digamos que o componente fique em src/components/SearchBar/ , o meu teste ficará em src/__tests__/components/SearchBar .

Teste 1 — Garantindo que o componente seja renderizado sem erros

Escolhido o local, crie o arquivo com o nome SearchBar.spec.js . Com o arquivo criado, podemos criar o nosso primeiro teste, ele ficará assim:

Como podemos ver, utilizei o enzyme para renderizar o meu componente, mais especificadamente o método render do enzyme, que basicamente irá renderizar o componente em um HTML estático. Além do render, temos o shallow e o mount, falaremos deles mais adiante, mas por ora, utilizaremos o render, que das 3 opções que temos, é o mais leve por não dar muito poder de fogo, o que não precisamos agora.

Para executar o teste, basta executar o seguinte comando no terminal:

npm test components/SearchBar/

O resultado deve ser esse:

Testes 2 e 3 — Testando o estado inicial do componente

Os próximos dois testes são bem parecidos, então farei numa tacada só aqui:

Nestes dois testes, utilizei o shallow, que é um intermediário entre o render e o mount, ele não é tão poderoso quanto o mount, mas para o que eu preciso é o suficiente. Diferente do render, o shallow me dá acesso ao estado do componente, podendo inclusive altera-lo, ou até buscar componentes internos e saber qual o estado do mesmo.

Teste 4 — Garantindo que a busca seja efetuada

Nosso próximo teste será para validar se o onChange do input está chamando nossa prop onChange passada como parâmetro.

Neste teste, tivemos uns recursos a mais, isso se justifica pelo fato de usarmos o window.setTimeout . Felizmente, a lib de testes que estamos usando, o jest , possui algumas mágicas para facilitar os testes nestes casos.

Como podemos ver, na linha 2 chamamos jest.useFakeTimers() , que basicamente irá substituir as implementações dos métodos: setTimeout, clearTimeout, setInterval, clearInterval. Que caso fossem necessários executar no nosso teste precisaríamos forçar um sleep de 300ms (que é bem ruim).

Logo depois, na linha 12, precisamos chamar o jest.runOnlyPendingTimers() que como o nome diz, irá executar as funções pendentes (respeitando o clearInterval/clearTimeout). Para mais detalhes dos Timer Mocks do jest, acesse https://facebook.github.io/jest/docs/timer-mocks.html.

Último teste — apenas uma busca por vez

E por último, precisamos de um teste que garanta que a função passada na prop onChange seja executada apenas quando o usuário concluir de digitar o que estiver buscando.

O teste é bem parecido com o anterior, com a diferença que simulamos diversas vezes o onChange do input, mas como podemos ver, após executar os timers pendentes, apenas o último foi executado.

Considerações sobre o primeiro exemplo

Este foi o primeiro exemplo, um simples componente com um input que dispara um evento toda vez que o valor for alterado, conseguimos criar 5 testes cobrindo os principais cenários.

Você pode ver o arquivo de testes completo neste link.

Exemplo 2 — ListHeroes.jsx

Este componente tem como objetivo listar todos os heróis buscados na API da Marvel. Ele usa a SearchBar para gerenciar as consultas e também recebe um gateway por dependência.

Teste 1 — Garantindo que o componente seja renderizado sem erros

Bom, vamos lá, criamos o arquivo de testes seguindo o mesmo padrão do anterior, e fazemos nosso primeiro teste, apenas para garantir que o mesmo foi renderizado corretamente.

Teste 2 — componentDidMount

Agora, precisamos garantir que quando o componente for iniciado, ou o evento componentDidMount for chamado, ele busque todos os heróis conforme o estado inicial da busca.

Para esse teste, precisamos de um setUp um pouco maior, criando um mock para o gateway a ser passado para o método. Repare, que neste teste utilizei o mount ao invés do shallow ou render, e o motivo para ter usado o mount, é que apenas ele passa pelo ciclo componentDidMount do componente, que é o que queremos testar aqui.

Testes 3 e 4— Sempre que o valor da busca for alterado, uma nova busca deve ser efetuada

Além de garantir que ao passar pelo componentDidMount , precisamos garantir que sempre que o valor de search no estado do componente for atualizado, e apenas quando for um novo valor, ele deve buscar os dados novamente na API da Marvel.

Assim garantimos que sempre que for salvo um novo termo de busca, o gateway será chamado e salvará o resultado no estado do componente.

Teste 5 — Simulando uma busca no SearchBar

Precisamos garantir também, que quando algo for alterado no componente de busca, deve salvar o novo termo no estado do componente para que seja efetuada uma nova busca na API.

Como já garantimos em outro teste que sempre que o valor de search for alterado no estado do componente irá efetuar uma nova busca na API, não precisamos dar o assert aqui que o método do gateway foi chamado.

Último teste — Renderizando os heróis

Agora podemos ir para o último teste:

Aqui, apenas garantimos que os heróis retornados na busca serão renderizados no nosso componente.

Você pode acessar a versão final do teste através deste link.

Conclusão

Como podemos ver, testar componentes React é relativamente fácil, as documentações, tanto do jest quanto do enzyme são bem completas, além da comunidade ser bem ativa também.

No próximo post, pretendo adicionar o Redux na aplicação e assim, vermos também os efeitos que teremos sobre os testes, tanto de componente de tela quanto os novos que surgirão.

--

--