Testes - Mockando métodos e propriedades de libs externas com Jest, no TypeScript

No desenvolvimento de software, muitas vezes nos deparamos com a necessidade de testar a integração do código que escrevemos com bibliotecas externas. Para isso, os testes devem ser limitados ao nosso código e é recomendado que as partes que utilizamos destas bibliotecas sejam mockadas, ou simuladas. Assim, não dependeremos de fatores alheios ao nosso controle, como o retorno de requisições feitas a API’s reais.

Ao considerar como estas simulações podem ser feitas, apresentarei exemplos usando o Jest como framework de testes, que será utilizado para mockar partes do Axios, uma biblioteca que efetua requisições HTTP. O código estará em Typescript. Veja o código abaixo:

import axios from 'axios’

jest.mock('axios')

const mockedAxios = axios as jest.Mocked<typeof axios>

mockedAxios.post.mockResolvedValue(axiosPostResponse)

A linha jest.mock('axios') cria um mock do módulo 'axios', substituindo as chamadas reais para o módulo por uma versão simulada, controlada pelos testes.

Em seguida, const mockedAxios = axios as jest.Mocked cria uma constante mockedAxios, que é do tipo jest.Mocked. Isso permite que a variável tenha acesso às funcionalidades de simulação fornecidas pelo Jest.

Por fim, mockedAxios.post.mockResolvedValue(axiosPostResponse) define o comportamento simulado da função post, do objeto mockedAxios. Neste caso, a simulação é configurada para retornar uma promessa (Promise) resolvida com o valor axiosPostResponse, que é o retorno simulado para fins de teste. Isso significa que quando o código chamar mockedAxios.post(), ele receberá uma promessa que, quando resolvida, retornará o valor axiosPostResponse.

Uma dúvida que poderá surgir:

Qual a necessidade da linha const mockedAxios = axios as jest.Mocked? Sabemos que depois de jest.mock('axios') qualquer referência ao axios estará apontando para o mock do módulo. Então, por que não usar diretamente axios.post.mockResolvedValue() para mockar um retorno para o método post?

Acontece que o mock do Axios, resultante de jest.mock('axios'), contém a assinatura do módulo do Axios, incluindo o método post. Porém, neste módulo não existe o método mockResolvedValue(), que é um recurso do Jest. Logo, axios.post.mockResolvedValue() seria impossível. A linha const mockedAxios = axios as jest.Mocked, assegura que a constante mockedAxios contenha um objeto com a assinatura daquele exportado pelo módulo do Axios (incluindo o método post) e, ao mesmo tempo, as funcionalidades do Jest, como mockResolvedValue. Então, será possível:

  • mockar o retorno de um método como axios.post() simplesmente usando mockedAxios.post.mockResolvedValue(axiosPostResponse).
  • mockar a implementação de qualquer método. Ex.: mockedAxios.getUri.mockImplementation(()=>"any_uri")

O mesmo pode ser feito com as propriedades do módulo. Com essas técnicas em mãos, você terá um controle total sobre as interações com o axios, tornando seus testes mais confiáveis e independentes de recursos externos.