You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 20:02:50