kYu*_*uZz 7 typescript jestjs ts-jest
我正在尝试监视我手动模拟的 ES6 类的方法。
我正在关注官方 Jest 文档,该文档仅针对 JavaScript。
这是我的代码:
// sound-player.ts
export default class SoundPlayer {
foo: string
constructor() {
this.foo = 'bar'
}
playFile(fileName: string) {
console.log(`Playing sound file: ${fileName}`)
}
}
Run Code Online (Sandbox Code Playgroud)
// __mocks__/sound-player.ts
export const mockPlayFile = jest.fn()
const mock = jest.fn().mockImplementation(() => {
return { playFile: mockPlayFile }
})
export default mock
Run Code Online (Sandbox Code Playgroud)
// sound-player-consumer.ts
import SoundPlayer from './sound-player'
export default class SoundPlayerConsumer {
soundPlayer: SoundPlayer
constructor(soundPlayer: SoundPlayer) {
this.soundPlayer = soundPlayer
}
playSomethingCool() {
this.soundPlayer.playFile('something-cool.mp3')
}
}
Run Code Online (Sandbox Code Playgroud)
// sound-player-consumer.test.ts
// @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')
})
Run Code Online (Sandbox Code Playgroud)
这些测试通过了,因为我使用了一个技巧。
请注意第一行sound-player-consumer.test.ts。如果没有这个,TypeScript 编译器会抱怨mockPlayFile没有被sound-player.ts.
对我来说,这意味着从 Jest 的角度来看一切都很好。
不过,我想从 TypeScript 的角度修复所有问题并删除@ts-ignore注释。
是的,这有点棘手和尴尬。
// @ts-ignore
import SoundPlayer, { mockPlayFile } from './sound-player'
Run Code Online (Sandbox Code Playgroud)
这里需要注释的原因是因为仅调用实际模块值(即 javascript)而不会重新连接@ts-ignore类型本身。jest.mock('./sound-player')
这仍然有效的原因@ts-ignore是因为使用mock,导入任何东西./sound-player就像从 导入一样__mocks__/sound-player.ts,减去类型。
那么如何解决这个问题呢?您可以mockPlayFile直接从__mocks__文件导入。这将使用实际类型来mockPlayFile减轻对@ts-ignore.
// sound-player-consumer.test.ts
import SoundPlayer from './sound-player'
import { mockPlayFile } from './__mocks__/sound-player'
import SoundPlayerConsumer from './sound-player-consumer'
jest.mock('./sound-player')
// ...
Run Code Online (Sandbox Code Playgroud)
或者,您也可以不导入mockPlayFile并使用类实例方法(即soundPlayer.mockPlayFile)。
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(soundPlayer.mockPlayFile).toHaveBeenCalledWith('something-cool.mp3')
})
Run Code Online (Sandbox Code Playgroud)
请注意,这仅是可能的,因为您在 中的类模拟实现之外定义
mockPlayFile为模拟。与定义对类模拟实现的每次调用相反,在这种情况下,模拟函数将有所不同。jest.fn__mocks__/sound-player.tsjest.fn
Jest 允许通过直接导出模拟类实现来手动模拟类,请参阅此处的文档示例。在你的情况下,你可以这样做...
// __mocks__/sound-player.ts
export const mockPlayFile = jest.fn();
export default class SoundPlayer {
foo: string = 'bar';
constructor () {
console.log('mocked SoundPlayer constructor');
}
playFile = mockPlayFile;
}
Run Code Online (Sandbox Code Playgroud)
我还没有测试所有的IRL,但我在过去几天做了类似的事情。LMK 如果有问题,我可以提供一个工作示例,可惜https://codesandbox.io不支持jest.mock.
| 归档时间: |
|
| 查看次数: |
1068 次 |
| 最近记录: |