При тестировании модуля, который имеет зависимость в другом файле. При назначении этого модуля в качестве jest.Mock
машинописного текста возникает ошибка, что метод mockReturnThisOnce
(или любой другой метод jest.Mock) не существует в зависимости, это потому, что он был ранее типизирован. Как правильно заставить машинописный текст наследовать типы от 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')
}
Мне кажется, что это очень распространенный вариант использования, и я не знаю, как правильно его вводить. Любая помощь приветствуется!
javascript
unit-testing
typescript
jestjs
Филип Чмальц
источник
источник
import
, оцениваются первыми, независимо от того, добавили ли вы какой-либо код перед импортом. Так что это не сработает.mock...
Ответы:
Вы можете использовать приведение типов, и оно
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;
Если вы не хотите использовать автоматический макет jest и предпочитаете создавать ручной
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
может быть либо классом, либо типом, либо интерфейсомисточник
jest.fn(() =>...
вместоjest.fn<TestedClassDependency>(() =>...
(я просто удалил приведение типов после jest.fn), потому что IntelliJ жалуется. В противном случае этот ответ мне помог, спасибо! Используя это в моем package.json: "@ types / jest": "^ 24.0.3"jest.mock('./SomeClass');
в приведенном выше коде?<jest.Mock<SomeClass>>SomeClass
Выражение вызывает ошибку TS для меня:Conversion of type 'typeof SomeClass' to type 'Mock<SomeClass, any>' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. Type 'typeof SomeClass' is missing the following properties from type 'Mock<SomeClass, any>': getMockName, mock, mockClear, mockReset, and 11 more.ts(2352)
Используйте
mocked
помощника,ts-jest
как описано здесь// foo.spec.ts import { mocked } from 'ts-jest/utils' import { foo } from './foo' jest.mock('./foo') // here the whole foo var is mocked deeply const mockedFoo = mocked(foo, true) test('deep', () => { // there will be no TS error here, and you'll have completion in modern IDEs mockedFoo.a.b.c.hello('me') // same here expect(mockedFoo.a.b.c.hello.mock.calls).toHaveLength(1) }) test('direct', () => { foo.name() // here only foo.name is mocked (or its methods if it's an object) expect(mocked(foo.name).mock.calls).toHaveLength(1) })
и если
tslint
ts-jest
находится в ваших dev-зависимостях,добавьте это правило в свой
tslint.json
:"no-implicit-dependencies": [true, "dev"]
источник
ts-jest
классов и: github.com/tbinna/ts-jest-mock-examples и этот пост: stackoverflow.com/questions/58639737/…Я использую шаблон из @ types / jest / index.d.ts чуть выше type def для Mocked (строка 515):
import { Api } from "../api"; jest.mock("../api"); const myApi: jest.Mocked<Api> = new Api() as any; myApi.myApiMethod.mockImplementation(() => "test");
источник
const myApi = new Api() as jest.Mocked<Api>;
jest.Mock<Api>
. Вам придется согласиться,const myApi = new Api() as any as jest.Mock<Api>
и я бы сказал, что приведенное выше выглядит немного лучше, чем двойное утверждение."strict": true
в виду наличие в tsconfig.json. Это касается таких вещей, какnoImplicitAny
иstrictNullChecks
т. Д., Поэтому вам не нужно устанавливать для них значение true.myApi
? Обычно он не заглушает все другие экземпляры, инициированные классомApi
в тестируемом модуле, верно?Есть два решения, оба используют желаемую функцию.
1) Используйте jest.MockedFunction
import * as dep from './dependency'; jest.mock('./dependency'); const mockMyFunction = dep.myFunction as jest.MockedFunction<typeof dep.myFunction>;
2) Используйте jest.Mock
import * as dep from './dependency'; jest.mock('./dependency'); const mockMyFunction = dep.default as jest.Mock;
Между этими двумя решениями нет разницы. Второй короче, поэтому я предлагаю использовать его.
Оба решения для кастинга позволяют вызывать любую шутливую фиктивную функцию на
mockMyFunction
лайкеmockReturnValue
илиmockResolvedValue
https://jestjs.io/docs/en/mock-function-api.htmlmockMyFunction.mockReturnValue('value');
mockMyFunction
может использоваться обычно для ожиданияexpect(mockMyFunction).toHaveBeenCalledTimes(1);
источник
В ролях
as jest.Mock
Простое приведение функции к
jest.Mock
должно помочь:(dep.default as jest.Mock).mockReturnValueOnce('return')
источник
Вот что я сделал с jest@24.8.0 и ts-jest@24.0.2 :
источник:
class OAuth { static isLogIn() { // return true/false; } static getOAuthService() { // ... } }
контрольная работа:
import { OAuth } from '../src/to/the/OAuth' jest.mock('../src/utils/OAuth', () => ({ OAuth: class { public static getOAuthService() { return { getAuthorizationUrl() { return ''; } }; } } })); describe('createMeeting', () => { test('should call conferenceLoginBuild when not login', () => { OAuth.isLogIn = jest.fn().mockImplementationOnce(() => { return false; }); // Other tests }); });
Вот как имитировать класс, не являющийся классом по умолчанию, и его статические методы:
jest.mock('../src/to/the/OAuth', () => ({ OAuth: class { public static getOAuthService() { return { getAuthorizationUrl() { return ''; } }; } } }));
Здесь должно быть какое-то преобразование типа из типа вашего класса в
jest.MockedClass
или что-то в этом роде. Но это всегда заканчивается ошибками. Я просто использовал его напрямую, и он работал.test('Some test', () => { OAuth.isLogIn = jest.fn().mockImplementationOnce(() => { return false; }); });
Но, если это функция, вы можете издеваться над ней и вести диалог типа.
jest.mock('../src/to/the/Conference', () => ({ conferenceSuccessDataBuild: jest.fn(), conferenceLoginBuild: jest.fn() })); const mockedConferenceLoginBuild = conferenceLoginBuild as jest.MockedFunction< typeof conferenceLoginBuild >; const mockedConferenceSuccessDataBuild = conferenceSuccessDataBuild as jest.MockedFunction< typeof conferenceSuccessDataBuild >;
источник
Я нашел это в
@types/jest
:/** * Wrap a function with mock definitions * * @example * * import { myFunction } from "./library"; * jest.mock("./library"); * * const mockMyFunction = myFunction as jest.MockedFunction<typeof myFunction>; * expect(mockMyFunction.mock.calls[0][0]).toBe(42); */
Примечание: когда вы делаете
const mockMyFunction = myFunction
что-то подобноеmockFunction.mockReturnValue('foo')
, вы тоже меняетесьmyFunction
.Источник: https://github.com/DefinitiTyped/DefinitiTyped/blob/master/types/jest/index.d.ts#L1089
источник
Недавняя библиотека решает эту проблему с помощью плагина babel: https://github.com/userlike/joke
Пример:
import { mock, mockSome } from 'userlike/joke'; const dep = mock(import('./dependency')); // You can partially mock a module too, completely typesafe! // thisIsAMock has mock related methods // thisIsReal does not have mock related methods const { thisIsAMock, thisIsReal } = mockSome(import('./dependency2'), () => ({ thisIsAMock: jest.fn() })); it('should do what I need', () => { dep.mockReturnValueOnce('return'); }
Имейте в виду, что
dep
иmockReturnValueOnce
полностью безопасны. Кроме того, tsserver знает, что онdepencency
был импортирован и назначен,dep
поэтому все автоматические рефакторинги, поддерживаемые tsserver, тоже будут работать.Примечание: я поддерживаю библиотеку.
источник