使用 Typescript 从 Jest 手动模拟中导入函数

Posted

技术标签:

【中文标题】使用 Typescript 从 Jest 手动模拟中导入函数【英文标题】:Import function from a Jest manual mock with Typescript 【发布时间】:2020-02-26 14:24:27 【问题描述】:

我正在 Typescript 项目中使用 Jest 创建一个自定义模拟(ES6 类)。模拟创建了几个mock.fn() 的最终导出,以便可以在测试套件中监视它们。

一个例子可以是来自 Jest 文档 (https://jestjs.io/docs/en/es6-class-mocks#manual-mock) 的官方例子。在那里,SoundPlayer 类已被模拟,因为它是它唯一的方法playSoundFile。该方法使用jest.fn() 模拟,导出以用于测试。

// soundPlayer.ts
export default class SoundPlayer 
  foo: string = 'bar';

  playSoundFile(filename: string) 
    console.log(`Playing sound file $filename`);
  

// __mocks__/soundPlayer.ts
export const mockPlaySoundFile = jest.fn();

const mock = jest.fn().mockImplementation(() => 
  return  playSoundFile: mockPlaySoundFile ;
);

export default mock;
// __tests__/soundPlayer.ts
import SoundPlayer,  mockPlaySoundFile  from '../soundPlayer';

jest.mock('../soundPlayer');

beforeEach(() => 
  mockPlaySoundFile.mockClear();
);

it('is called with filename', () => 
  const filename = 'song.mp3';
  const soundPlayer = new SoundPlayer();
  soundPlayer.playSoundFile(filename);

  expect(mockPlaySoundFile).toBeCalledWith(filename);
);

测试按预期工作,但 TS 在尝试导入模拟的 mockPlaySoundFile 函数时通知错误(这对我来说很有意义)。那是因为mockPlaySoundFile 显然不存在于soundPlayer.ts 中。但是由于 jest.mock('../soundPlayer'); 模拟是在后台导入的,因此导出确实存在。

有没有办法通知 TS 在这种情况下查看模拟?

【问题讨论】:

这个线程有很多很好的例子来说明如何做到这一点:***.com/questions/48759035/… 你可以使用来自 ts-jest 的 mocked 助手:github.com/kulshekhar/ts-jest/blob/master/docs/user/… 【参考方案1】:

我有同样的问题,我只有一个解决方法。我的问题是我从节点手动模拟 fs。

所以我有一个“fs”的手动模拟,大致如下:

const fs = jest.genMockFromModule("fs");

let mockFiles = ;

function __clearMocks()
    mockFiles = ;


module.exports = fs;

很明显,当我的测试用例导入 fs 时它不起作用:

import * as fs from 'fs';
fs.__clearMocks();

为了让它工作,我创建了一个类型的扩展:

declare module 'fs' 
    export function __clearMocks(): void; 

所以现在我可以像这样修改我的测试用例:

import * as fs from 'fs';
import 'fsExtension';
fs.__clearMocks();

希望对您有所帮助!

【讨论】:

【参考方案2】:

解决此问题的最简单方法是使用ts-jest 的mocked() 助手。助手将确保您可以访问模拟测试方法。 __tests__/soundPlayer.ts 将如下所示:

// __tests__/soundPlayer.ts
import  mocked  from "ts-jest/utils";
import SoundPlayer from '../soundPlayer';

jest.mock('../soundPlayer');

const soundPlayer = mocked(new SoundPlayer());

beforeEach(() => 
  soundPlayer.playSoundFile.mockClear();
);

it('is called with filename', () => 
  const filename = 'song.mp3';

  soundPlayer.playSoundFile(filename);

  expect(soundPlayer.playSoundFile).toBeCalledWith(filename);
);

如果你真的想包含mockPlaySoundFile,你可以通过告诉 Typescript 编译器抑制导入错误来做到这一点:

// @ts-ignore
import  mockPlaySoundFile  from '../soundPlayer';

另外,请查看我的仓库中的示例:https://github.com/tbinna/ts-jest-mock-examples,尤其是您的 soundPlayer 示例:https://github.com/tbinna/ts-jest-mock-examples/tree/master/sound-player

【讨论】:

以上是关于使用 Typescript 从 Jest 手动模拟中导入函数的主要内容,如果未能解决你的问题,请参考以下文章

在 Typescript 中使用 Jest + Supertest 进行模拟

使用正确类型使用 Jest 和 Typescript 模拟 Express 请求

如何避免 Typescript 转译 Jest __mocks__

使用 jest typescript 模拟 Axios 的类型

用 Jest 和 Typescript 模拟一个全局对象

使用 typescript 模拟 forwardRef 组件 jest mockImplementation