Craftsmanship - Mock em Testes com React e jest

16 mai 2024

Na nossa jornada pelo desenvolvimento de software, a garantia da qualidade do código é essencial. Valorizamos a abordagem do "craftsmanship", onde cada desenvolvedor se empenha na excelência técnica e na busca contínua pela melhoria. Uma prática comum que adotamos é o Test-Driven Development (TDD), onde escrevemos testes antes de implementar o código, garantindo assim que as funcionalidades estejam corretas desde o início.

Neste artigo, vamos explorar algumas técnicas de teste para aplicações desenvolvidas em React, utilizando a poderosa ferramenta Jest. Vamos nos aprofundar em exemplos práticos, abordando diferentes cenários e mostrando como podemos escrever testes eficientes para garantir a qualidade do nosso código.

Mocking de Hooks em Aplicações React

No desenvolvimento de aplicações React, frequentemente utilizamos hooks para gerenciar o estado e o comportamento dos componentes. Quando estamos testando componentes que fazem uso desses hooks, é crucial garantir que os testes sejam precisos e confiáveis. Um método comum para testar tais componentes é através do uso de mocks, que são substituições simuladas para os módulos reais. Vamos explorar como podemos usar o Jest para mockar um hook chamado useUser, que retorna uma interface específica contendo propriedades e métodos associados a um usuário.

1000005764

Para mockar o hook useUser, primeiro importamos o mesmo no contexto do arquivo de teste. Em seguida, utilizamos a função jest.mock() para substituir o módulo useUser por uma versão simulada. Isso garante que, em vez de utilizar a implementação real do hook, estaremos utilizando uma versão controlada que podemos configurar de acordo com nossas necessidades de teste.

Em seguida, dentro do bloco describe, definimos vários casos de teste para demonstrar como podemos mockar diferentes aspectos do hook useUser.

  • Test 1: Mock de Valor de Retorno Constante: No primeiro caso de teste, configuramos o mock para retornar um objeto contendo um nome de usuário específico, utilizando a função mockReturnValue. Isso simula o comportamento do hook ao fornecer um valor estático para a propriedade username. Em seguida, renderizamos o componente Page1 e verificamos se o nome de usuário fornecido está visível na tela.
  • Test 2: Mock de Chamada de Função: No segundo caso de teste, criamos uma função de mock utilizando jest.fn() e a configuramos como o valor de retorno para a propriedade someAction do hook useUser. Em seguida, renderizamos o componente Page1 e simulamos um evento de clique em um botão específico. Posteriormente, verificamos se a função de mock foi chamada exatamente uma vez.
  • Test 3: Mock de Função que Recebe Parâmetros: No terceiro caso de teste, além de configurar uma função de mock, também simulamos a passagem de parâmetros para essa função. Definimos o valor de retorno para a propriedade anotherAction, que recebe dois parâmetros, e verificamos se a função de mock foi chamada corretamente com esses parâmetros específicos.
  • Test 4: Mock de Ação Assíncrona: No último caso de teste, configuramos uma função assíncrona de mock para a propriedade asyncAction, utilizando jest.fn() e Promise.resolve(). Em seguida, renderizamos o componente Page1 e simulamos um evento que dispara essa ação assíncrona. Finalmente, verificamos se a função assíncrona de mock foi chamada exatamente uma vez.

10000057651000005766

Neste exemplo, mostramos como podemos mockar diferentes aspectos de um hook, como valores de retorno, funções e até mesmo ações assíncronas. Isso nos permite testar o comportamento dos componentes de forma isolada e previsível.

Mocking de Hooks que recebe parâmetros

Neste caso de teste, usamos jest.mockImplementation() para fornecer uma implementação customizada para o hook useTeam. Esta implementação recebe um parâmetro id e retorna um objeto Team com um nome específico, dependendo do valor de id. Se id for igual a '1', o nome do time será 'First Squad'; caso contrário, será 'Second Squad'.

Após configurar o mock do hook, renderizamos o componente Page3 para testar como ele lida com o retorno do hook useTeam para diferentes valores de id.

Por fim, verificamos se o componente renderizado exibe corretamente o nome do time esperado com base no parâmetro passado para o hook.

1000005767

Utilizando Tuplas em Hooks React

Ao trabalhar com React, frequentemente nos deparamos com hooks que retornam tuplas, como é o caso do useCopyToClipboard. Vamos explorar como podemos lidar com esse tipo de hook e testar seu comportamento em diferentes cenários.

Primeiramente, definimos o tipo CopyToClipboardTuple, que representa a estrutura da tupla retornada pelo hook. Neste caso, a tupla é composta por um valor booleano indicando se o texto foi copiado com sucesso e uma função para lidar com a cópia do texto.

Para testar o comportamento do hook em diferentes cenários, criamos mocks utilizando o Jest.

No primeiro caso de teste, simulamos que o texto foi copiado com sucesso (isCopied é true) e renderizamos o componente Page2. Utilizando waitFor, aguardamos a renderização do componente e então verificamos se o texto "Copied is true" está visível na tela. Em seguida, simulamos que a cópia do texto falhou (isCopied é false) e novamente verificamos se o texto "Copied is false" é exibido após a renderização do componente.

No segundo caso de teste, mockamos o retorno do hook com isCopied como true e definimos um mock para a função handleCopy. Renderizamos o componente Page2 e simulamos um clique em um botão específico. Verificamos se a função handleCopy foi chamada com o texto esperado ('My name’') quando o botão foi clicado. 

1000005768

Neste caso, ao chamar o hook devemos obrigatoriamente criar a const com os dois valores

1000005769

1000005773

Neste exemplo, mostramos como podemos mockar um hook que retorna uma tupla e testar seu comportamento em diferentes situações, como valores de retorno e chamadas de função

Testando Componentes com Componentes Filhos

Em muitas aplicações React, encontramos componentes que possuem outros componentes como filhos. Vamos ver como podemos testar esses casos, utilizando mocks para os componentes filhos e garantindo que o componente pai se comporte corretamente. 

1000005774

Neste exemplo, mostramos como podemos fazer mock de um componente filho e testar qual é o valor do parâmetro showButton que o componente  pai está passando.

Estratégias Avançadas de Mocking em Aplicações React

Por fim, vamos explorar algumas estratégias avançadas de mocking em aplicações React, como mockar hooks useState usados multiplas vezes.

1000005797

10000058021000005803

Detalhes do Mock

1- jest.mock('react', () => ({...}): Esta linha está utilizando a função jest.mock() do Jest para substituir o módulo 'react' por uma versão simulada. A função jest.mock() recebe dois parâmetros: o primeiro é o caminho para o módulo que está sendo mockado ('react' neste caso), e o segundo é uma função que retorna o conteúdo do mock.

2- {...jest.requireActual('react'), ...}: Aqui, estamos utilizando o operador spread (...) para mesclar as propriedades do módulo real 'react' com as propriedades do objeto de mock. jest.requireActual('react') é uma função do Jest que retorna o módulo real 'react', permitindo-nos acessar suas funcionalidades originais. Portanto, estamos basicamente copiando todas as propriedades e métodos do módulo real 'react' para o objeto de mock.

3- useState: jest.fn(): Esta linha está substituindo a implementação do hook useState do módulo 'react' por uma função mock criada pelo Jest, usando jest.fn(). Isso significa que, ao importar e utilizar o hook useState em um arquivo de teste, ele não irá chamar a implementação real do hook, mas sim o mock criado pelo Jest. O mock de useState será uma função simulada que pode ser controlada e rastreada durante os testes.



Padrões de Nomenclatura e Organização de Testes

Além de escrever testes eficientes, é importante também adotar padrões de nomenclatura e organizar os testes de forma adequada. Isso torna o código mais legível e facilita a manutenção no futuro. Aqui estão algumas notas sobre o padrão de nomenclatura e organização de testes que podemos adotar em nossos projetos.

Padrão de Nomenclatura

Para garantir clareza e consistência nos nomes dos testes, podemos seguir o padrão MethodName_StateUnderTest_ExpectedBehavior. Este padrão descreve de forma sucinta o método ou função que está sendo testado, o estado em que está sendo testado e o comportamento esperado.

Por exemplo, se estivermos testando uma função de soma simples, poderíamos ter um nome de teste como sum_TwoNumbers_ReturnsSum.

Organização de Testes

Quanto à organização dos testes, devemos evitar adicionar "describe" extras. Os testes do Jest são automaticamente file scoped, o que significa que um arquivo que executa um único teste não precisa de um bloco de descrição dentro dele. No entanto, podemos adicionar múltiplos "describe" para agrupar testes relacionados dentro de um arquivo.

Além disso, podemos utilizar o padrão "Given When Then" para agrupar e organizar os testes de forma mais descritiva. Este padrão divide os testes em três partes:

  • Given: Descreva o estado inicial ou as pré-condições para o teste.
  • When: Descreva a ação ou o evento que está sendo testado.
  • Then: Descreva o resultado esperado do teste.

Na IDE podemos facilmente ver o grupo de testes da seguinte forma:

 

Separando Give, When, Then utilizando o describe

1000005804

1000005805

Sem describe, apenas nomeando corretamente:

1000005806

Conclusão

Esperamos que este artigo tenha sido útil para você entender como podemos escrever Mocks e testes eficientes para aplicações React utilizando Jest. Testes são uma parte fundamental do processo de desenvolvimento de software, e com as técnicas certas, podemos garantir a qualidade e a robustez do nosso código. Continue praticando e explorando novas técnicas, com certeza existem muito mais do que estas apresentadas, e não se esqueça do poder do "Craftsmanship" no desenvolvimento de software.