音频时长获取异常:测试中span内容始终为空的问题排查
音频播放器组件测试场景下时间显示不更新的问题
我开发了songPlayerTimeComponent音频播放器组件,原本预期通过监听事件和loadedmetadata事件来更新current_time_song和full_time_song这两个span的内容,但在测试环境中,这两个元素始终显示构造函数初始化的空字符串,明明已经绑定了loadedmetadata事件。
组件核心代码
export default class songPlayerTimeComponent { constructor () { this.songSelected = {}; this.currentTime = " " this.timeSong = " " this.apiService = new SongService() } initialize (element) { element.innerHTML = ` <span id="current_time_song"></span> <audio controls> <source type="audio/mpeg"> </audio> <span id="full_time_song"></span>` this.currentTimeEL = element.querySelector("#current_time_song") this.audioEl = element.querySelector('audio') this.timeSongEl = element.querySelector("#full_time_song") document.body.addEventListener('changeSongEvent', (event) => this.changeSongEvent(event.detail.songId)) document.body.addEventListener('playSongEvent', () => this.playSongEvent()) document.body.addEventListener('pauseSongEvent', () => this.pauseSongEvent()) document.body.addEventListener('stopSongEvent', () => this.stopSongEvent()) this.audioEl.addEventListener("play", () => this.songHasPlayed()); this.audioEl.addEventListener("pause", () => this.songHasStopped()) this.audioEl.addEventListener("ended", () => this.songHasEnded()) this.audioEl.addEventListener("timeupdate", () => this.getCurrentTime()) this.audioEl.addEventListener('loadedmetadata', () => { this.getSongTime() this.renderComponent() } ) } getCurrentTime(){ let seconds = parseInt(this.audioEl.currentTime % 60); let minutes = parseInt((this.audioEl.currentTime / 60) % 60); if (seconds < 10) { seconds = '0' + seconds; } if (minutes < 10) { minutes = '0' + minutes } this.currentTime = minutes + ':' + seconds; } async changeSongEvent (songId) { try{ this.songSelected = await this.apiService.getSong(songId); this.audioEl.src = "./songs/" + this.songSelected.fileName; await this.audioEl.load() } catch (error) { this.displayErrorMessage (error) } } getSongTime() { const duration = parseInt(this.audioEl.duration) let seconds = duration % 60 let minutes = Math.floor(duration / 60); seconds = ("0" + seconds).slice(-2); minutes = ("0" + minutes).slice(-2); this.timeSong = minutes + ":" + seconds; this.getCurrentTime(); } }
测试代码
const songPlayerTimeComponent = new SongPlayerTimeComponent() const songSelected = songs[0] songPlayerTimeComponent.initialize(componentEl) document.body.dispatchEvent( new CustomEvent('changeSongEvent', { detail: { songId: songSelected.id } }) ) await flushPromises() const currentTimeEL = document.querySelector('#current_time_song') expect(currentTimeEL.innerHTML).toEqual("9:00")
测试报错信息
AssertionError: expected '' to deeply equal '00:00' ❯ tests/songPlayerTimeComponent.test.js:61:42 59| const timeSongEL = document.querySelector('#full_time_song') 60| expect(timeSongEL.innerHTML).toBeDefined() 61| expect(timeSongEL.innerHTML).toEqual("00:00") | ^ 62| }) 63| - Expected "00:00" + Received "" ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/2]⎯ FAIL tests/songPlayerTimeComponent.test.js > songPlayerTimeComponent > success > Sur réception de l'événement global changeSongEvent, la durée totale de la chanson doit s'afficher. AssertionError: expected '' to deeply equal '9:00' ❯ tests/songPlayerTimeComponent.test.js:79:45 77| 78| const currentTimeEL = document.querySelector('#current_time_song') 79| expect(currentTimeEL.innerHTML).toEqual("9:00") | ^ 80| }) 81| - Expected "9:00" + Received ""
问题原因及解决方法
核心问题
renderComponent方法缺失:代码中监听loadedmetadata事件时调用了this.renderComponent(),但组件未实现该方法,导致计算后的this.currentTime和this.timeSong无法同步到DOM元素。- 测试未等待音频加载流程:
flushPromises()仅处理Promise异步,音频的loadedmetadata事件是独立的异步流程,测试代码未等待该事件触发就执行断言,自然拿不到更新后的内容。
修复步骤
1. 实现renderComponent方法
在组件中添加该方法,将数据同步到DOM:
renderComponent() { this.currentTimeEL.textContent = this.currentTime; this.timeSongEl.textContent = this.timeSong; }
同时在getCurrentTime方法末尾调用该方法,确保播放时的时间更新能实时同步:
getCurrentTime(){ // 原有时间计算逻辑 this.currentTime = minutes + ':' + seconds; this.renderComponent(); // 新增该行 }
2. 测试代码中等待loadedmetadata事件
修改测试代码,等待音频元数据加载完成后再执行断言:
const songPlayerTimeComponent = new SongPlayerTimeComponent() const songSelected = songs[0] songPlayerTimeComponent.initialize(componentEl) // 用Promise包裹等待loadedmetadata事件 const waitForLoadedMetadata = new Promise(resolve => { songPlayerTimeComponent.audioEl.addEventListener('loadedmetadata', resolve, { once: true }); }) document.body.dispatchEvent( new CustomEvent('changeSongEvent', { detail: { songId: songSelected.id } }) ) await flushPromises() await waitForLoadedMetadata // 等待音频元数据加载完成 const currentTimeEL = document.querySelector('#current_time_song') expect(currentTimeEL.innerHTML).toEqual("00:00") // 初始播放时间应为00:00,若需测试特定时间需手动设置audioEl.currentTime
3. 额外优化
- 构造函数中初始化
this.currentTime和this.timeSong为"00:00"而非空格,避免初始显示异常。 - 测试环境中可Mock
SongService返回的音频数据,或确保测试用音频文件可正常访问,避免因文件加载失败导致loadedmetadata事件不触发。
内容的提问来源于stack exchange,提问作者Nicolas Pellerin
相关产品推荐
相关产品推荐

