Машинопись и шутка: избегайте ошибок типа на издевающихся функциях
Когда вы хотите издеваться над внешними модулями с Jest, мы можем использовать метод jest.mock()
для автоматического jest.mock()
функций на модуле.
Затем мы можем манипулировать и опросить насмешливые функции на нашем издеваемом модуле по своему желанию.
Например, рассмотрим следующий надуманный пример для издевательств над модулем 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);
});
Вышеупомянутое будет отлично работать в Jest, но будет вызывать ошибку TypScript:
Свойство 'mockReturnValueOnce' не существует в типе '(url: string, config?: AxiosRequestConfig | undefined) => AxiosPromise'.
В typedef для axios.get
праву не входит свойство mockReturnValueOnce
. Мы можем заставить axios.get
обрабатывать axios.get
как литерал объекта, обернув его как Object(axios.get)
, но:
Каков идиоматический способ издеваться над функциями при сохранении безопасности типов?
Ответы
Ответ 1
Добавьте эту строку кода const mockedAxios = axios as jest.Mocked<typeof axios>
. А затем используйте mockedAxios для вызова mockReturnValueOnce. С вашим кодом должно быть сделано так:
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);
});
Ответ 2
Для идиоматического макета функции при сохранении безопасности типов используйте spyOn в сочетании с 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();
});
Ответ 3
Обычный подход для предоставления новых функциональных возможностей для импорта для расширения исходного модуля, такого как declare module "axios" {... }
. Это не лучший выбор здесь, потому что это должно быть сделано для всего модуля, в то время как mocks может быть доступен в одном тесте и быть недоступным в другом.
В этом случае подход, основанный на типе, заключается в утверждении типов, где это необходимо:
(axios.get as jest.Mock).mockReturnValueOnce({ data: expectedResult });
...
expect(axios.get as jest.Mock).toHaveBeenCalled();
Ответ 4
Пожалуйста, используйте функцию mocked
из ts-jest
import myModuleThatCallsAxios from '../myModule';
import axios from 'axios';
jest.mock('axios');
// OPTION - 1
const mockedAxios = mocked(axios, true)
// your original 'it' block
it('Calls the GET method as expected', async () => {
const expectedResult: string = 'result';
mockedAxios.mockReturnValueOnce({ data: expectedResult });
const result = await myModuleThatCallsAxios.makeGetRequest();
expect(mockedAxios.get).toHaveBeenCalled();
expect(result).toBe(expectedResult);
});
// OPTION - 2
// wrap axios in mocked at the place you use
it('Calls the GET method as expected', async () => {
const expectedResult: string = 'result';
mocked(axios).get.mockReturnValueOnce({ data: expectedResult });
const result = await myModuleThatCallsAxios.makeGetRequest();
// notice how axios is wrapped in 'mocked' call
expect(mocked(axios).get).toHaveBeenCalled();
expect(result).toBe(expectedResult);
});
Я не могу подчеркнуть, насколько велик mocked
, больше не было кастинга.
Ответ 5
Будет ли это работать так же с функцией? Я пытаюсь смоделировать node-fetch
безуспешно :(