Jest로 Vue/Nuxt 컴포넌트 테스트 만들기 (Store, Routes Params 사용)

Nuxt.js·2021년 9월 1일
0

NUXT기초 배우기

목록 보기
15/20

배경

현대 프로그래밍에서 자동화 테스트는 개발자들의 디버깅 시간을 아껴주고 동시에 생산성을 높여 주고 있으므로 필수라고 할 수 있겠다. 이후에 Jenkins 등의 CI/CD 툴과 연계 하면 그 시너지는 엄청나게 커진다.
프론트엔드 개발에서도 마찬가지인데 Nuxt.js나 Vue.js 프로젝트에 초기단계부터 Jest테스팅을 도입 하는 것은 탁월한 선택이다.
하지만 현업에서 개발중인 프로젝트에 테스트를 적용 하고자 하면 구체적인 방법론에서 예제가 많이 부족 하다.
대표적인 예로 URL에서 파라메터를 받고, Vuex Store를 사용해서 API에 엑세스 해서 데이터를 획득 하는 등, 실제 움직임과 비슷한 환경에서 테스팅을 도입 하는 방법을 알아보자.

문제점

  • 스토어 목킹이 복잡하고 특히 규모가 커서 모듈화 되어 있으면 이야기가 복잡해진다.
  • 컴포넌트를 테스트 하자.
  • 컴포넌트 테스트에 파라메터를 넘기고 싶다.
  • 컴포넌트 테스트에 프로퍼티(Props)를 넘기고 싶다.
  • 컴포넌트 테스트에 렌더링 된 텍스트를 검사 하고 싶다.

해결방법

  • 전체 스토어를 실제 빌드해서 사용 한다. → 지난포스팅을 참고 하면 된다.

  • 컴포넌트 테스트를 만드는 방법 : 위 스토어가 구현되었다는 가정하에 아래 코드와 같다.

// yarn test tests/jest/Genres.spec.js
import Vuex from 'vuex'
import Vuetify from 'vuetify'
import { createLocalVue, shallowMount } from '@vue/test-utils'
import Genres from '@/pages/genres/index'

describe('[장르목록] 유닛테스트', () => {
  const localVue = createLocalVue()
  localVue.use(Vuex)
  let NuxtStore
  let store
  let wrapper

  beforeAll(async () => {
    const storePath = `${process.env.buildDir}/store.js`
    NuxtStore = await import(storePath)
  })

  beforeEach(async () => {
    store = await NuxtStore.createStore()
  })

  beforeEach(async () => {
    const vuetify = new Vuetify()
    const route = {
      path: '/genres/15',
      params: { id: 15 },
    }
    wrapper = await shallowMount(Genres, {
      localVue,
      vuetify,
      store,
    })
  })

  afterEach(() => {
    wrapper.destroy()
  })

  describe('장르 목록 컴포넌트 테스트 ...', () => {
    test('인스턴스 로드 성공 해야함', () => {
      expect(wrapper.element).toMatchSnapshot()
    })
  })
})

이후에는 마지막 test() 부분에 입맛에 맞는 여러 케이스의 테스팅을 추가 하면 된다.

  • 컴포넌트 테스트에 파라메터를 넘기는 방법을 알아본다.
    예를 들어 /genres/15 인 URL로 접근 하는 경우를 생각 해보자.

    위에서 블록지정 한 부분을 추가 하면 된다. (컴포넌트는 pages/genres/_id.vue에 작성 했을 것이다.)

  • 컴포넌트 테스트에 프로퍼티를 넘기는 방법을 알아본다.
    Props나 Computed(filterd) 등을 넘기는 방법도 위와 유사하다. 코드는 아래와 같다.

  • 컴포넌트 테스트에 렌더링 된 텍스트를 검사 하는 방법을 알아본다.

위 모든 내용을 적용한 소스코드는 아래와 같을 것이다.

// yarn test tests/jest/Genres.spec.js
import Vuex from 'vuex'
import Vuetify from 'vuetify'
import { createLocalVue, shallowMount } from '@vue/test-utils'
import ShowGenre from '@/pages/genres/_id.vue'

describe('[장르목록] 유닛테스트', () => {
  const localVue = createLocalVue()
  localVue.use(Vuex)
  let NuxtStore
  let store
  let wrapper

  beforeAll(async () => {
    const storePath = `${process.env.buildDir}/store.js`
    NuxtStore = await import(storePath)
  })

  beforeEach(async () => {
    store = await NuxtStore.createStore()
  })

  beforeEach(async () => {
    const vuetify = new Vuetify()
    const route = {
      path: '/genres/15',
      params: { id: 15 },
    }
    wrapper = await shallowMount(Genres, {
      localVue,
      vuetify,
      store,
      mocks: {
        $route: route,
      },
      propsData: { genre: { id: 15, desc: 'test genre' } },
      computed: {
        user() {
          return { id: 1, name: 'test user 1' }
        },
      },
    })
  })

  afterEach(() => {
    wrapper.destroy()
  })

  describe('장르 목록 컴포넌트 테스트 ...', () => {
    test('인스턴스 로드 성공 해야함', () => {
      expect(wrapper.element).toMatchSnapshot()
    })

    test('본문에 테스트1 이라는 텍스트가 포함 되어야 함.', () => {
        expect(wrapper.text()).toMatch('테스트1')
    })

    test('testVariable1 이라는 변수가 true 여야 한다.', () => {
      expect(wrapper.vm.testVariable1).toBe(true)
    })
    
  })
})

참고

  • shallowMount vs mount 차이점
    mount는 일반적으로 Vue컴포넌트를 마운트 하는 것으로 실 사용과 큰 차이가 없다.
    shallowMount 는 해당 컴포넌트에 포함된 자식 컴포넌트를 포함 하지 않는다. 따라서 더욱 빠르고 단순하지만 커버리지는 낮아지게 된다. 코드 재사용성과 캡슐화 측면에서는 shallowMount로 각 컴포넌트를 커버하는게 더 좋은 테스트가 되겠다.

총평

이렇게 예제로 테스트를 하나 만들어 보았다. 하지만 프로젝트에는 수많은 컴포넌트가 존재하게 될 것이고 수없이 추가/변경 될 것이다. 그에 맞춰서 테스트도 하나하나 추가/수정 해주자. 당장의 테스트 하나를 짜는 것은 귀찮은 작업이지만 이후에 생기는 수많은 삽질을 미연에 방지 해준다.

profile
Nuxt.js

0개의 댓글