如何用Jest和TypeScript监视手动Mock的ES6类方法并消除TS类型报错?
解决TypeScript下Jest手动Mock类时移除
@ts-ignore的问题 方法1:扩展原模块的类型声明
TypeScript支持扩展已有模块的类型,我们可以在测试文件中为./sound-player模块补充mockPlayFile的导出类型,让编译器识别这个导出项:
修改测试文件sound-player-consumer.test.ts:
// 扩展原模块的类型声明 declare module './sound-player' { export const mockPlayFile: jest.Mock; } // 无需@ts-ignore,正常导入 import SoundPlayer, { mockPlayFile } from './sound-player' import SoundPlayerConsumer from './sound-player-consumer' jest.mock('./sound-player') const MockedSoundPlayer = SoundPlayer as jest.Mock<SoundPlayer> beforeEach(() => { MockedSoundPlayer.mockClear() mockPlayFile.mockClear() }) // 测试用例保持不变 it('We can check if the consumer called the class constructor', () => { const soundPlayer = new MockedSoundPlayer() const soundPlayerConsumer = new SoundPlayerConsumer(soundPlayer) expect(SoundPlayer).toHaveBeenCalledTimes(1) }) it('We can check if the consumer called a method on the class instance', () => { const soundPlayer = new MockedSoundPlayer() const soundPlayerConsumer = new SoundPlayerConsumer(soundPlayer) soundPlayerConsumer.playSomethingCool() expect(mockPlayFile).toHaveBeenCalledWith('something-cool.mp3') })
这种方式无需修改原文件或Mock文件,仅在测试文件中临时补充类型,适合快速修复。
方法2:直接从Mock目录导入类型
既然jest.mock会将原模块替换为__mocks__下的版本,我们可以直接从Mock目录导入类型和mock方法,再做类型断言:
修改测试文件:
// 直接从Mock目录导入类型与mock方法 import SoundPlayerMock, { mockPlayFile } from './__mocks__/sound-player' import SoundPlayerConsumer from './sound-player-consumer' import type SoundPlayer from './sound-player' jest.mock('./sound-player') // 断言原模块的默认导出就是Mock类 const MockedSoundPlayer = SoundPlayerMock as jest.Mock<SoundPlayer> beforeEach(() => { MockedSoundPlayer.mockClear() mockPlayFile.mockClear() }) // 测试用例保持不变 it('We can check if the consumer called the class constructor', () => { const soundPlayer = new MockedSoundPlayer() const soundPlayerConsumer = new SoundPlayerConsumer(soundPlayer) expect(SoundPlayerMock).toHaveBeenCalledTimes(1) }) it('We can check if the consumer called a method on the class instance', () => { const soundPlayer = new MockedSoundPlayer() const soundPlayerConsumer = new SoundPlayerConsumer(soundPlayer) soundPlayerConsumer.playSomethingCool() expect(mockPlayFile).toHaveBeenCalledWith('something-cool.mp3') })
这种方式逻辑更清晰,明确依赖Mock的类型,避免类型不匹配问题。
方法3:重构Mock文件对齐原类类型
调整__mocks__/sound-player.ts,让Mock类完全对齐原类的类型结构,同时导出mock方法:
import type SoundPlayer from '../sound-player'; export const mockPlayFile = jest.fn(); // 创建符合原类类型的Mock类 const SoundPlayerMock: jest.Mock<SoundPlayer> = jest.fn().mockImplementation(() => ({ foo: 'bar', // 保留原类的属性 playFile: mockPlayFile, })); export default SoundPlayerMock;
然后在测试文件中正常导入即可:
import SoundPlayer, { mockPlayFile } from './sound-player' import SoundPlayerConsumer from './sound-player-consumer' jest.mock('./sound-player') const MockedSoundPlayer = SoundPlayer as jest.Mock<typeof import('./__mocks__/sound-player').default> beforeEach(() => { MockedSoundPlayer.mockClear() mockPlayFile.mockClear() }) // 测试用例保持不变
这种方式让Mock文件本身就与原类类型严格对齐,减少后续类型断言的复杂度,适合大型项目。
总结
三种方案都能解决mockPlayFile未导出的TypeScript编译错误,可根据项目需求选择:
- 方法1适合快速修复,无需改动其他文件;
- 方法2逻辑更直观,明确依赖Mock资源;
- 方法3让Mock类型更严谨,利于长期维护。
内容的提问来源于stack exchange,提问作者kYuZz
相关产品推荐
相关产品推荐

