web-dev-qa-db-fra.com

Typographie et plaisanterie: éviter les erreurs de type sur les fonctions simulées

Lorsque vous souhaitez simuler des modules externes avec Jest, nous pouvons utiliser la méthode jest.mock() pour simuler automatiquement les fonctions d'un module.

Nous pouvons ensuite manipuler et interroger les fonctions simulées sur notre module simulé comme nous le souhaitons.

Par exemple, considérons l'exemple artificiel suivant pour se moquer du module axios:

import myModuleThatCallsAxios from '../myModule';
import axios from 'axios';

jest.mock('axios');

it('Calls the GET method as expected', async () => {
  const expectedResult: string = 'result';

  axios.get.mockReturnValueOnce({ data: expectedResult });
  const result = await myModuleThatCallsAxios.makeGetRequest();

  expect(axios.get).toHaveBeenCalled();
  expect(result).toBe(expectedResult);
});

Ce qui précède fonctionnera correctement dans Jest mais générera une erreur TypeScript:

La propriété 'mockReturnValueOnce' n'existe pas sur le type '(url: string, config ?: AxiosRequestConfig | undefined) => AxiosPromise'.

Le typedef pour axios.get N'inclut pas à juste titre une propriété mockReturnValueOnce. Nous pouvons forcer TypeScript à traiter axios.get Comme un littéral d'objet en l'encapsulant comme Object(axios.get), mais:

Quelle est la manière idiomatique de se moquer des fonctions tout en préservant la sécurité du type?

14
duncanhall

Ajoutez cette ligne de code const mockedAxios = axios as jest.Mocked<typeof axios>. Et puis utilisez le mockedAxios pour appeler le mockReturnValueOnce. Avec votre code, devrait être fait comme ceci:

import myModuleThatCallsAxios from '../myModule';
import axios from 'axios';

jest.mock('axios');
const mockedAxios = axios as jest.Mocked<typeof axios>;

it('Calls the GET method as expected', async () => {
  const expectedResult: string = 'result';

  mockedAxios.get.mockReturnValueOnce({ data: expectedResult });
  const result = await myModuleThatCallsAxios.makeGetRequest();

  expect(mockedAxios.get).toHaveBeenCalled();
  expect(result).toBe(expectedResult);
});
22
hutabalian

Pour se moquer idiomatiquement de la fonction tout en conservant la sécurité de type, utilisez spyOn en combinaison avec mockReturnValueOnce :

import myModuleThatCallsAxios from '../myModule';
import axios from 'axios';

it('Calls the GET method as expected', async () => {
  const expectedResult: string = 'result';

  // set up mock for axios.get
  const mock = jest.spyOn(axios, 'get');
  mock.mockReturnValueOnce({ data: expectedResult });

  const result = await myModuleThatCallsAxios.makeGetRequest();

  expect(mock).toHaveBeenCalled();
  expect(result).toBe(expectedResult);

  // restore axios.get
  mock.mockRestore();
});
12

Une approche habituelle pour fournir de nouvelles fonctionnalités aux importations pour étendre le module d'origine comme declare module "axios" { ... }. Ce n'est pas le meilleur choix ici, car cela devrait être fait pour l'ensemble du module, tandis que les maquettes peuvent être disponibles dans un test et non disponibles dans un autre.

Dans ce cas, une approche sécurisée consiste à affirmer les types si nécessaire:

  (axios.get as jest.Mock).mockReturnValueOnce({ data: expectedResult });
  ...
  expect(axios.get as jest.Mock).toHaveBeenCalled();
6
Estus Flask