본문 바로가기
테스트코드

📰 hls 테스트 코드 작성

by frontChoi 2024. 10. 1.
반응형

📁 HLS(HTTP 라이브 스트리밍) 테스트 목록

  • 재생을 누르면 노래가 시작된다
  • 중지를 누르면 노래가 중지가된다
  • 노래가 끝나면, 1.5초 이후 다시 처음부터 시작한다

위와같이 내용은 간단하다. 블로그 전용이므로 복잡한로직은 제거하고, 순수하게 재생/중지/노래가끝났을때의 처리 정도만 실시하였다.

 

 

아래는 원본코드이며, m3u8 url를 가진 주소를 이용하여 $VideoPlayer를 이용하여 노래를 재생/중지를 이용한다. 

($VideoPlayer 안에는 hls.js에서 사용할수 있는 기능들을 객체로 만들어놓았고, $VideoPlayer 따로 만든 이유는 어느 화면에서든 공통에서 작성할 수 있도록 하기위해 globalProperties 이용하여 만들었다.)

$VideoPlayer는 아래와 같은 기능을 가지고 있다

  • setVideo : 비디오 element를 설정하는 함수이며, setVideo(document.querySelector('#streaming')) 형태로 사용할 수 있고, 추가적으로 getVideo를 통해서 return된 video를 획득할수있다.
  • registerEventListener : video의 이벤트 등록를 해주는 함수이다.  $VideoPlayer.registerEventListener('pause', pauseEventListener) 처럼 사용하여, 두번째 인자에 함수를 넣어줄수 있다.
  • playerMusic : m3u8확장자를 가진 url를 넣을수있으며, video src에 넣을수있다.
  • startLoad : hls.js에서 autoStartLoad:false를 두는 경우 startLoad에 -1를 두고 play가 가능하다.
  • onPlay : video의 play함수를 호출하며 return을 Promise형태로 return한다.
  • onPause : vidoe pause 함수를 호출하며 video가 중지된다
<template>
  <div>
    <button id="playbtn" @click="clickPlay">재생</button>
    <button id="pausebtn" @click="clickPause">중지</button>
    <p>{{ currentTime }} / {{ durationTime }}</p>
  </div>
</template>
<script setup>
import { getCurrentInstance, ref } from 'vue'
const app = getCurrentInstance()
const $VideoPlayer = app.appContext.config.globalProperties.$VideoPlayer

const m3u8Url = ref(
  'm3u8확장자를 가진 URL'
)

const currentTime = ref(0)
const durationTime = ref(0)
const pauseEventListener = () => {
  console.log('pause')
}
const playEventListener = () => {
  console.log('play')
}

const timeUpdateEventListener = (event) => {
  currentTime.value = event.target.currentTime
  if (event.target.duration) {
    durationTime.value = event.target.duration
  }
}

const endedEventListener = () => {
  setTimeout(() => {
    $VideoPlayer.onPlay()
  }, 1500)
}

$VideoPlayer.setVideo(document.querySelector('#streaming'))
$VideoPlayer.registerEventListener('pause', pauseEventListener)
$VideoPlayer.registerEventListener('play', playEventListener)
$VideoPlayer.registerEventListener('timeupdate', timeUpdateEventListener)
$VideoPlayer.registerEventListener('ended', endedEventListener)
const clickPlay = () => {
  $VideoPlayer.playerMusic(m3u8Url.value)
  $VideoPlayer.startLoad(-1)
  $VideoPlayer.onPlay()
}

const clickPause = () => {
  $VideoPlayer.onPause()
}
</script>
<style scoped src="@/assets/styles/hls/hlsview.css"></style>

 

🤪 재생을 누르면 노래가 시작된다

재생버튼을 누르면 노래가 시작된다. 순서는 id가 playBtn인 버튼을 찾는다 => playBtn인 버튼을 click시켜준다 => clickPlay함수안에 playerMusic, startLoad , onPlay 가 호출됐는지 확인한다. 식으로 작성한다

($VideoPlayer는 외부의존성이 있기 때문에 mocking 처리 하였다. 여기서는 $VideoPlayer의 내용물에대해 관심이 없다 전제하에 작성한다.)

 

추가적으로 playerMusic,startLoad같은 경우 인자와 함께 호출되었으므로 toBeCalledWith 함수를 사용하여 테스트한다.

import { shallowMount } from '@vue/test-utils'
import HlsView from '../../HlsView.vue'

const $VideoPlayer = {
  setVideo: jest.fn(),
  registerEventListener: jest.fn(),
  playerMusic: jest.fn(),
  startLoad: jest.fn(),
  onPlay: jest.fn(),
  onPause: jest.fn()
}

describe('hlsview unit test', () => {
  let wrapper = null
  jest.useFakeTimers()
  beforeEach(() => {
    wrapper = shallowMount(HlsView, {
      global: {
        config: {
          globalProperties: {
            $VideoPlayer
          }
        }
      }
    })
  })
  afterEach(() => {
    $VideoPlayer.onPlay.mockClear()
    jest.clearAllTimers()
  })
  test('재생을 누르면 노래가 시작된다', async () => {
    //given(준비) : id가 playBtn인 버튼을 찾는다
    const playBtn = wrapper.find('#playbtn')
    //when(실행) : playBtn인 버튼을 click시켜준다
    await playBtn.trigger('click')
    //then(검증) : clickPlay함수안에 playerMusic, startLoad , onPlay 가 호출됐는지 확인한다
    expect($VideoPlayer.playerMusic).toBeCalledWith(
      'm3u8 확장자를 가진 URL'
    )
    expect($VideoPlayer.startLoad).toBeCalledWith(-1)
    expect($VideoPlayer.onPlay).toBeCalled()
  })
})

 

🥹  중지를 누르면 노래가 중지가된다

중지버튼을 누르면 노래가 중지가 된다. 마찬가지로 id가 pauseBtn인 버튼을 찾고 => pauseBtn인 버튼을 클릭한다 => onPause의 호출 여부를 확인한다.

 

import { shallowMount } from '@vue/test-utils'
import HlsView from '../../HlsView.vue'

const $VideoPlayer = {
  setVideo: jest.fn(),
  registerEventListener: jest.fn(),
  playerMusic: jest.fn(),
  startLoad: jest.fn(),
  onPlay: jest.fn(),
  onPause: jest.fn()
}

describe('hlsview unit test', () => {
  let wrapper = null
  jest.useFakeTimers()
  beforeEach(() => {
    wrapper = shallowMount(HlsView, {
      global: {
        config: {
          globalProperties: {
            $VideoPlayer
          }
        }
      }
    })
  })
  afterEach(() => {
    $VideoPlayer.onPlay.mockClear()
    jest.clearAllTimers()
  })

  test('중지를 누르면 노래가 중지가된다', async () => {
    //given(준비) : id가 pauseBtn인 버튼을 찾는다
    const pausebtn = wrapper.find('#pausebtn')
    //when(실행)  : id가 pauseBtn인 버튼을 클릭한다
    await pausebtn.trigger('click')
    //then(검증) : onPause함수 호출여부를 확인한다.
    expect($VideoPlayer.onPause).toBeCalled()
  })

})

 

🙃 노래가 끝나면, 1.5초 이후 다시 처음부터 시작한다

노래가 끝나고 1.5초가 지나면 노래가 다시 시작한다. 이미 endedEventListener 를 이벤트리스너로 등록하였고, 종료가 되면 endedEventListener 라는 함수가 발동이 될것이다.

import { shallowMount } from '@vue/test-utils'
import HlsView from '../../HlsView.vue'

const $VideoPlayer = {
  setVideo: jest.fn(),
  registerEventListener: jest.fn(),
  playerMusic: jest.fn(),
  startLoad: jest.fn(),
  onPlay: jest.fn(),
  onPause: jest.fn()
}

describe('hlsview unit test', () => {
  let wrapper = null
  // setTimeOut,setInterval를 사용할 수 있도록 useFakeTimers를 최상위에 호출한다
  jest.useFakeTimers()
  beforeEach(() => {
    wrapper = shallowMount(HlsView, {
      global: {
        config: {
          globalProperties: {
            $VideoPlayer
          }
        }
      }
    })
  })
  afterEach(() => {
    $VideoPlayer.onPlay.mockClear()
    //  단위테스트가 끝날때마다 clearAllTimers를 시켜준다
    jest.clearAllTimers()
  })

  test('노래가 끝나면, 1.5초 이후 다시 처음부터 시작한다', async () => {
    //given : endedEventListener를 가진 vm을 호출한다
    const instance = wrapper.vm
    //when : endedEventListener를 호출한다 && 1.5초가 흐르도록 설정한다
    instance.endedEventListener()
    jest.advanceTimersByTime(1500)
    //then : onPlay가 호출됐는지 확인한다.
    expect($VideoPlayer.onPlay).toBeCalled()
  })
})
반응형

댓글