Vue 3 테스트

테스트 주의사항

컴포저블을 목업(mock)하는 경우의 ref 관리

Vue 3 컴포저블을 테스트하는 일반적인 패턴은 이러한 파일들이 반환하는 ref 또는 computed 값을 목업하는 것입니다.

다음과 같은 데모 컴포저블을 고려해봅시다:

export const useCounter = () => {
  const counter = ref(1)
  const increase = () => { counter.value += 1 }

  return { counter, increase }
}

현재 이러한 컴포저블을 사용하고 카운터를 노출하는 컴포넌트가 있다면 해당 기능을 포함하는 테스트를 작성하고 싶을 것입니다. 이러한 간단한 예제와 같은 경우, 컴포저블을 전혀 목업하지 않고도 통과할 수 있지만, Tanstack Query 래퍼나 Apollo 래퍼와 같이 더 복잡한 기능의 경우 jest.mock을 활용해야 할 수도 있습니다.

이러한 경우에는 테스트 파일에서 컴포저블을 목업해야 합니다:

<script setup>
const { counter, increase } = useCounter()
</script>

<template>
  <p>매우 유용한 카운터: {{ counter }}</p>
  <button @click="increase">+</button>
</template>
import { ref } from 'vue'
import { useCounter } from '~/composables/useCounter'

jest.mock('~/composables/useCounter')

describe('MyComponent', () => {
  const increaseMock = jest.fn()
  const counter = ref(1)

  beforeEach(() => {
    useCounter.mockReturnValue({
      increase: increaseMock,
      counter
    })
  })

  describe('카운터가 2 일 때', () => {
    beforeEach(() => {
      counter.value = 2
      createComponent()
    })

    it('...', () => {})
  })

  it('기본값은 1 이어야 합니다', () => {
    createComponent()

    expect(findSuperUsefulCounter().text()).toBe(1)
    // 실패
  })
})

위의 예에서 볼 수 있듯이, 컴포저블에 의해 반환되는 함수와 counter ref의 목업을 같이 생성하고 있지만, 매우 중요한 단계가 누락된 것입니다.

counter 상수는 ref이며, 따라서 매번 우리가 이를 수정할 때마다 할당한 값이 유지됩니다. 위의 예에서 두 번째 it 블록은 이전 테스트 중 어느 것에서든 할당된 값이 유지될 것이므로 실패할 것입니다.

이 문제를 해결하고 권장 사항은 가장 상위 수준의 beforeEach 블록에서 항상 ref를 재설정하는 것입니다.

import { ref } from 'vue'
import { useCounter } from '~/composables/useCounter'

jest.mock('~/composables/useCounter')

describe('MyComponent', () => {
  const increaseMock = jest.fn()

  // 더욱 신중하게하기 위해 `undefined`로 초기화할 수 있습니다
  const counter = ref(undefined)

  beforeEach(() => {
    counter.value = 1
    useCounter.mockReturnValue({
      increase: increaseMock,
      counter
    })
  })

  describe('카운터가 2 일 때', () => {
    beforeEach(() => {
      counter.value = 2
      createComponent()
    })

    it('...', () => {})
  })

  it('기본값은 1 이어야 합니다', () => {
    createComponent()

    expect(findSuperUsefulCounter().text()).toBe(1)
    // 통과
  })
})