티스토리 뷰

728x90

create-nuxt-app로 프로젝트를 설정할 때 단위 테스트 프레임워크를 선택할 수 있다. AVA, Jest 중에 선택할 수 있는데 React.js로 개발했을 때 사용한 Jest를 사용한 경험이 있었기 때문에 Jest로 선택했다. 참고로 Jest는 facebook에서 만들었다.

선택하지 않은 경우에는 테스트 관련 모듈을 수동으로 설치 및 설정해줘야 한다. 아래 모듈을 --save-dev로 추가하자.

  • @vue/test-utils : vue 코드를 단위 테스트할 때 필요한 유틸을 제공한다.
  • jest : 단위 테스트 작성에 필요한 matcher, teardown 등을 제공한다. mock, spy로 테스트하는 방법도 안내하고 있으니 공식 사이트를 방문해서 꼭 보도록 하자. 
  • vue-jest , babel-jest : vue, ES6 문법으로 작성된 코드를 jest 수행 시 변환해주는 플러그인 모듈입니다.

그리고 package.json에 scripts를 추가하자.

📄 package.json

  "scripts": {
  	...
    "test": "jest"
  },

 

jest.config.js 파일을 생성한 후 아래 설정을 추가 하자.

📄 jest.config.js

module.exports = {
  moduleNameMapper: {
    '^@/(.*)$': '<rootDir>/$1',
    '^~/(.*)$': '<rootDir>/$1',
    '^vue$': 'vue/dist/vue.common.js'
  },
  moduleFileExtensions: ['js', 'vue', 'json'],
  transform: {
    '^.+\\.js$': 'babel-jest',
    '.*\\.(vue)$': 'vue-jest'
  },
  collectCoverage: true,
  collectCoverageFrom: [
    '<rootDir>/components/**/*.vue',
    '<rootDir>/pages/**/*.vue'
  ]
}

 

test 폴더를 생성한 후 간단한 테스트 파일을 만든다.

📄 test/TodoList.test.js

describe('test', () => {
  test('test for test', () => {
    expect(2 + 2).toBe(4)
  })
})

 

npm run test로 실행했을 때 아래와 같은 화면을 만난다면 단위 테스트 환경 구축이 완료된 것이다.

 

추가로 Vuex 테스트하려면 몇 가지 설정이 필요하다. 테스트 코드에 import 사용하려면 바벨 설정을 해줘야 한다.

. babelrc 파일을 생성하고 아래처럼 설정하자.

 

📌참고
create-nuxt-app설정 시 테스트 모듈을 선택했다면 이미 설정이 되어있다.

📄 .babelrc

{
  "env": {
    "test": {
      "presets": [
        [
          "@babel/preset-env",
          {
            "targets": {
              "node": "current"
            }
          }
        ]
      ]
    }
  }
}

 

설정을 하지 않은 경우 import 에러를 만날 수 있다.

글로벌 Vue를 오염시키지 않고 테스트 내부에서 사용하는 Vue를 생성하고 연결해야 하기 때문에 test-utils의 createLocalVue 메서드를 이용해야 한다.

 

📄 test/TodoList.test.js

import { createLocalVue, shallowMount } from '@vue/test-utils'
import Vuex from 'vuex'
import TodoList from '~/components/TodoList'

const localVue = createLocalVue()
localVue.use(Vuex)

describe('TodoList Component', () => {
  let store

  beforeEach(() => {
    const state = {
      todos: [],
      visibility: 'all',
      selectedTodo: null
    }

    store = new Vuex.Store({
      state
    })
  })

  test('should render data', () => {
    shallowMount(TodoList, {
      store, localVue
    })

    expect(true).toBe(true)
  })
})

 

실행하면 아래와 같은 에러를 만난다.

Vuex.Store 시 state만 추가하고 TodoList에서 사용하는 getters가 존재하지 않기 때문이다. 구현한 스토어를 재사용하고 싶어서 getters, actions를 아래처럼 추가했다.

📄 test/TodoList.test.js

import { createLocalVue, shallowMount } from '@vue/test-utils'
import Vuex from 'vuex'
import { getters, actions } from '~/store'
import TodoList from '~/components/TodoList'
const localVue = createLocalVue()
localVue.use(Vuex)

describe('TodoList Component', () => {
  let store

  beforeEach(() => {
    const state = {
      todos: [{
        id: 'todo-1584926194640',
        text: 'API 방식으로 변경하기',
        completed: true
      }],
      visibility: 'all',
      selectedTodo: null
    }

    store = new Vuex.Store({
      state,
      getters,
      actions
    })
  })

  test('should render data', () => {
    const wrapper = shallowMount(TodoList, {
      store,
      localVue
    })

    expect(wrapper.findAll('li').length).toBe(1)
  })
})

 

만약 store가 복잡하게 구성된 경우라면 아래 포스팅을 참고해서 테스트용 빌더를 만들어 보는 것도 괜찮은 방법 같다.

 

How to Test Nuxt Stores with Jest

The Quick Answer

medium.com

todos 데이터가 없는 경우에는 정상 동작하지만 1개의 데이터를 추가한 후 테스트를 수행하면 아래와 같은 에러를 만난다.

 

 

nuxt-link  컴포넌트가 뭔지 모르겠다는 건데 해결 방법은 test-utils에서 제공해주는 RouterLinkStub 사용해서 nuxt-link를 stub 처리하는 것이다.  자세한 내용은 링크를 참고하자.

 

최종 코드는 다음과 같다.

📄 test/TodoList.test.js

import { createLocalVue, shallowMount, RouterLinkStub } from '@vue/test-utils'
import Vuex from 'vuex'
import { getters, actions } from '~/store'
import TodoList from '~/components/TodoList'
const localVue = createLocalVue()
localVue.use(Vuex)

describe('TodoList Component', () => {
  let store

  beforeEach(() => {
    const state = {
      todos: [{
        id: 'todo-1584926194640',
        text: 'API 방식으로 변경하기',
        completed: true
      }],
      visibility: 'all',
      selectedTodo: null
    }

    store = new Vuex.Store({
      state,
      getters,
      actions
    })
  })

  test('should render data', () => {
    const wrapper = shallowMount(TodoList, {
      store,
      localVue,
      stubs: {
        NuxtLink: RouterLinkStub
      }
    })

    expect(wrapper.findAll('li').length).toBe(1)
  })
})

728x90
댓글