Testes unitários Frontend - Como Mockar Módulos e React Hooks usando apenas o Jest
Published: 2021-12-30 16:25:17

Olá Pessoal! Nesse post eu vou explicar algumas maneiras que você pode adotar para realizar os mocks dos seus testes unitários.

## Mockando imports de módulos

Caso as funções ou componentes que você deseja mockar nos seus testes venham de um módulo externo, existe uma maneira de utilizar apenas o objeto Jest para realizar o mock. 
Exemplo, digamos que queremos testar se nosso componente está chamando corretamente a função `push` retornada pelo hook `useHistory `do módulo `react-router`. Nós podemos mockar a função `push `e usá-la no `expect ` do nosso teste para ter certeza que ela foi chamada, para fazer isso nos devemos:

- Criar uma função de mock usando o método do Jest `jest.fn()` que retorna uma função de simulação OU criar nossa própria implementação

```
   // OPÇÃO 1
   const mockPush = jest.fn()
  // OPÇÃO 2
  const mockPush = () => {
    // alguma funcionalidade...
    // return algo
  }
```
 - Mockar o import do módulo `react-router` no ínicio do arquivo antes de iniciar os testes, dessa forma toda vez que um componente renderizado pelo Jest durante o teste tentar importar esse módulo, o Jest substituirá o real conteúdo por aquele que nos mockamos

```
  jest.mock('react-router', () => ({
  ...jest.requireActual('react-router'),
  useHistory: () => ({
    push: mockPush
  })
}))
```

🎯 Note que usamos o método `requireActual` do objeto Jest que retorna o módulo real e com o spread operator copiamos tudo do módulo verdadeiro e apenas subsstituimos o que precisávamos testar, nesse caso o hook `useHistory` e seu retorno.

[Caso queira saber mais sobre o objeto Jest e seus métodos, recomendo muito que você leia a documentação do Jest](https://jestjs.io/pt-BR/docs/jest-object)

 - Agora, ao escrever seu teste unitário você deve utilizar a função mockada `mockPush` para conferir o resultado com o expect, e pronto!

```
await waitFor(() => expect(mockPush).toHaveBeenCalledWith('sua-rota'))
```

## Mockando React Hooks
Em testes unitários de componentes React, é muito comum precisarmos mockar Hooks para realizar verificações de comportamento em nossos componentes. Caso você deseja mockar apenas a implementação de um hook, também é possível utilizar apenas o objeto Jest para realizar nosso teste.

 1. Primeiro você deve importar todo o arquivo que contém seu hook, o que retornará um objeto que contém todos os retornos desse arquivo

```
import * as MeuHook from 'path-para-seu-hook'
```

 - No ínicio do arquivo antes de iniciar os testes mockaremos UM dos retornos desse arquivo, nesse caso o hook, usando o método do Jest `jest.spyOn` que cria uma função de mock similar ao `jest.fn` mas também observa todas as chamadas pelo objectDoArquivo[nomeDoMétodo]

  -  O primeiro parâmetro que passamos ao jest.spyOn é justamente o  objeto contendo todos os retornos do nosso arquivo 
  -  E o segundo parâmetro é o nome do método (o nome do hook ou função) que é exportado desse arquivo e  queremos observar (note, caso seu hook seja exportado através de um `export default` em vez de fornecermos o seu nome, passaremos 'default' como parâmetro)
```
  jest.spyOn(MeuHook, 'useNomeDoMeuHook')
```
  **_Agora temos duas opções, podemos mockar a implementação do hook para todo o nosso arquivo de teste, ou seja, para cada test case ele retornará o mesmo valor, OU, podemos realizar uma implementação diferente para cada test case_**
  2.1 Caso queiramos a mesma implementação para todo o arquivo, podemos realizar a declaração junto com método `spyOn` e todos os nossos tests cases terão acesso ao mesmo mock
```
  jest.spyOn(MeuHook, 'useNomeDoMeuHook').mockImplementation(() => 
  {
    //  implemente aqui
  })
```
2.2 Caso contrário, guardaremos o retorno do spyOn em uma constante 
```
  const useNomeDoMeuHookMock = jest.spyOn(MeuHook, 'useNomeDoMeuHook')
```
E dentro de cada test case realizaremos a diferente implementação
```
test('It should ....', async () => {
  useNomeDoMeuHookMock.mockReturnValue(// valor retornado pelo hook)
})

```
E por fim, não podemos nos esquecer de depois de cada teste limpar o mock para não sujar as implementações dos próximos testes, por isso colocaremos no inicio do nosso suite de testes a função `afterEach` para resetar nosso mock

```
describe('...', () => {
  afterEach(() => {
      useNomeDoMeuHookMock.mockClear()
})

  test()....
})
```


**Obrigada por ter lido até aqui e espero que eu tenha te ajudado! Qualquer dúvida ou sugestão fique a vontade para entrar em contato comigo 😀**