Макетная зависимость в шутке с помощью typescript

При тестировании модуля, который имеет зависимость в другом файле. При присвоении этому модулю jest.Mock typescript появляется ошибка, что метод mockReturnThisOnce (или любой другой метод jest.Mock) не существует в зависимости, это потому, что он был ранее введен. Каков правильный способ получить typescript для наследования типов из jest.Mock?

Вот краткий пример.

Зависимость

const myDep = (name: string) => name;
export default myDep;

test.ts

import * as dep from '../depenendency';
jest.mock('../dependency');

it('should do what I need', () => {
  //this throws ts error
  // Property mockReturnValueOnce does not exist on type (name: string)....
  dep.default.mockReturnValueOnce('return')
}

Я чувствую, что это очень распространенный случай использования и не уверен, как правильно его напечатать. Любая помощь приветствуется!

Ответ 1

Вы можете использовать приведение типов, и ваш test.ts должен выглядеть следующим образом:

import * as dep from '../dependency';
jest.mock('../dependency');

const mockedDependency = <jest.Mock<typeof dep.default>>dep.default;

it('should do what I need', () => {
  //this throws ts error
  // Property mockReturnValueOnce does not exist on type (name: string)....
  mockedDependency.mockReturnValueOnce('return');
});

Транспортер TS не знает, что jest.mock('../dependency'); меняет тип dep, поэтому вы должны использовать приведение типов. Поскольку импортированный dep не является определением типа, вы должны получить его тип с помощью typeof dep.default.

Вот некоторые другие полезные паттерны, которые я нашел во время моей работы с Jest и TS

Когда импортируемый элемент является классом, вам не нужно использовать typeof, например:

import { SomeClass } from './SomeClass';

jest.mock('./SomeClass');

const mockedClass = <jest.Mock<SomeClass>>SomeClass;

Это решение также полезно, когда вам нужно смоделировать некоторые собственные узлы модулей:

import { existsSync } from 'fs';

jest.mock('fs');

const mockedExistsSync = <jest.Mock<typeof existsSync>>existsSync;

Если вы не хотите использовать автоматический шутник и предпочитаете создавать ручной режим

import TestedClass from './TestedClass';
import TestedClassDependency from './TestedClassDependency';

const testedClassDependencyMock = jest.fn<TestedClassDependency>(() => ({
  // implementation
}));

it('Should throw an error when calling playSomethingCool', () => {
  const testedClass = new TestedClass(testedClassDependencyMock());
});

testedClassDependencyMock() создает экземпляр макета объекта TestedClassDependency может быть классом, типом или интерфейсом

Ответ 2

Я использую шаблон из @types/jest/index.d.ts чуть выше определения типа для Mocked (строка 515):

import { Api } from "../api";
jest.mock("../api");

const myApi: jest.Mocked<Api> = new Api() as any;
myApi.myApiMethod.mockImplementation(() => "test");

Ответ 3

Поскольку мы говорим о тесте, быстрый и грязный способ состоит в том, чтобы просто сказать TypeScript игнорировать эту строку:

//@ts-ignore
dep.default.mockReturnValueOnce('return')