如何用Jasmine模拟window的Audio类测试Angular服务的play方法
解决Angular服务中模拟window.Audio并验证play调用的问题
你的问题核心是没有将Audio的mock关联到服务内部的_audio实例,服务里依然使用了原生的new Audio(),导致你的audioMock完全没被用到。下面是修正后的完整测试方案:
步骤1:Mock依赖的SoundPreferencesStore
首先需要mock服务的构造依赖SoundPreferencesStore,因为测试中不需要真实的store实例:
import { TestBed } from '@angular/core/testing'; import { Service } from './your-service-path'; // 替换为实际的服务路径 import { SoundPreferencesStore } from 'app/shared/stores/sound-preferences.store'; describe('Service', () => { let service: Service; let soundPreferencesStoreMock: jasmine.SpyObj<SoundPreferencesStore>; let audioSpy: jasmine.SpyObj<HTMLAudioElement>; const originalAudio = window.Audio; // 保存原生Audio,避免污染其他测试 beforeEach(() => { // 1. Mock SoundPreferencesStore soundPreferencesStoreMock = jasmine.createSpyObj('SoundPreferencesStore', ['isSoundMuted']); soundPreferencesStoreMock.isSoundMuted.and.returnValue(false); // 默认返回未静音 // 2. Mock全局的window.Audio audioSpy = jasmine.createSpyObj('Audio', ['play']); // 让play返回resolved Promise,避免服务中的catch捕获到错误 audioSpy.play.and.returnValue(Promise.resolve()); // 重写window.Audio,让它返回我们的spy对象 window.Audio = jasmine.createSpy('Audio').and.returnValue(audioSpy); // 3. 配置TestBed并实例化服务 TestBed.configureTestingModule({ providers: [ Service, { provide: SoundPreferencesStore, useValue: soundPreferencesStoreMock } ] }); service = TestBed.inject(Service); }); // 测试后恢复原生Audio,防止影响其他测试用例 afterEach(() => { window.Audio = originalAudio; }); describe('playSound', () => { it('should call Audio.play() when sound is not muted', async () => { // Arrange service.isSoundMuted = false; // Act await service.playSound(); // 等待play的Promise完成 // Assert expect(audioSpy.play).toHaveBeenCalled(); }); it('should NOT call Audio.play() when sound is muted', () => { // Arrange service.isSoundMuted = true; // Act service.playSound(); // Assert expect(audioSpy.play).not.toHaveBeenCalled(); }); }); });
关键说明
- 全局mock window.Audio:因为服务构造函数中直接执行
new Audio(),必须在服务实例化前重写全局的Audio构造函数,让它返回我们的spy对象,这样服务内部的_audio就是我们的mock实例。 - 处理Promise:
Audio.play()返回Promise,测试中需要用async/await等待Promise完成,否则断言可能在Promise resolve前执行,导致测试失败。 - 恢复原生Audio:在
afterEach中还原window.Audio,避免这个mock影响其他测试用例的执行。 - 覆盖两种场景:分别测试静音和非静音的情况,确保逻辑正确性。
内容的提问来源于stack exchange,提问作者WowBlueSky
相关产品推荐
相关产品推荐

