如何在Jest中正确对Audio API进行Mock模拟
规范模拟 Audio API 的实现方案
你现有实现的核心思路是可行的,只要调整几个细节就能符合Jest测试的规范,避免全局污染、无法断言调用情况的问题。
核心优化方向
- 不要直接继承原生Audio类,测试环境下调用super构造函数可能触发JSDOM的兼容异常,全量模拟API即可
- 所有模拟方法都用
jest.fn()包装,支持直接断言调用次数、参数,不需要额外写状态校验逻辑 - 全局对象挂载统一用
Object.defineProperty,适配原生属性不可写的场景 - 原型方法覆盖用
jest.spyOn实现,支持测试后自动恢复原生实现,不会污染其他用例
完整实现代码
import "regenerator-runtime/runtime"; import { AudioState } from '../src'; // 独立实现全量模拟的MockAudio类 export class MockAudio { src: string = ''; state: AudioState = AudioState.STOPPED; duration: number = NaN; playing: boolean = false; currentTime: number = 0; fastSeek = jest.fn((time: number): Promise<void> => { this.currentTime = time; return Promise.resolve(); }); play = jest.fn((): Promise<void> => { this.playing = true; this.state = AudioState.PLAYING; return Promise.resolve(); }); pause = jest.fn((): void => { this.playing = false; this.state = AudioState.PAUSED; }); // 按需补全其他用到的Audio API,比如事件监听相关方法 addEventListener = jest.fn(); removeEventListener = jest.fn(); } // 规范替换全局Audio对象 Object.defineProperty(window, 'Audio', { writable: true, configurable: true, value: MockAudio }); // 规范模拟MediaSource Object.defineProperty(window, 'MediaSource', { writable: true, configurable: true, value: jest.fn().mockImplementation(() => ({ addEventListener: jest.fn(), removeEventListener: jest.fn(), addSourceBuffer: jest.fn(), endOfStream: jest.fn(), readyState: 0 })) }); // 用spyOn覆盖HTMLMediaElement原型方法,支持后续恢复 jest.spyOn(HTMLMediaElement.prototype, 'play').mockResolvedValue(); jest.spyOn(HTMLMediaElement.prototype, 'pause').mockResolvedValue();
使用注意事项
- 每个测试用例结束后可以调用
jest.clearAllMocks()清空所有mock的调用记录,避免用例之间的状态干扰 - 如果部分测试需要用到原生Audio实现,可以在对应测试块内调用
Object.defineProperty临时还原,测试结束后再恢复mock即可 - 可以直接在测试用例中对mock方法做断言,比如
expect(MockAudio.prototype.play).toHaveBeenCalledTimes(1),验证业务逻辑是否正确调用了Audio API
内容的提问来源于stack exchange,提问作者pilotguy
相关产品推荐
相关产品推荐

