본문 바로가기

책/Vue.js 퀵스타트

단위 테스트

Mocha 플러그인을 활용한 단위 테스트

프로젝트 생성 시 수동 설정을 하면 단위 테스트를 사용할 수 있음 

C:\JetBrains\vscode_workspace>vue create test1
Vue CLI v4.5.9
? Please pick a preset: (Use arrow keys)
  Default ([Vue 2] babel, eslint)
  Default (Vue 3 Preview) ([Vue 3] babel, eslint)
> Manually select features
  

Vue CLI v4.5.9
? Please pick a preset: Manually select features
? Check the features needed for your project:
 (*) Choose Vue version
 (*) Babel
 ( ) TypeScript
 ( ) Progressive Web App (PWA) Support
 ( ) Router
 ( ) Vuex
 ( ) CSS Pre-processors
 (*) Linter / Formatter
>(*) Unit Testing
 ( ) E2E Testing
 
 Vue CLI v4.5.9
? Please pick a preset: Manually select features
? Check the features needed for your project: Choose Vue version, Babel, Linter, Unit
? Choose a version of Vue.js that you want to start the project with (Use arrow keys)
> 2.x
  3.x (Preview)
  
  Vue CLI v4.5.9
? Please pick a preset: Manually select features
? Check the features needed for your project: Choose Vue version, Babel, Linter, Unit
? Choose a version of Vue.js that you want to start the project with 2.x
? Pick a linter / formatter config: (Use arrow keys)
> ESLint with error prevention only
  ESLint + Airbnb config
  ESLint + Standard config
  ESLint + Prettier
  
  Vue CLI v4.5.9
? Please pick a preset: Manually select features
? Check the features needed for your project: Choose Vue version, Babel, Linter, Unit
? Choose a version of Vue.js that you want to start the project with 2.x
? Pick a linter / formatter config: Basic
? Pick additional lint features: (Press <space> to select, <a> to toggle all, <i> to invert selection)
>(*) Lint on save
 ( ) Lint and fix on commit
 
 Vue CLI v4.5.9
? Please pick a preset: Manually select features
? Check the features needed for your project: Choose Vue version, Babel, Linter, Unit
? Choose a version of Vue.js that you want to start the project with 2.x
? Pick a linter / formatter config: Basic
? Pick additional lint features: Lint on save
? Pick a unit testing solution: (Use arrow keys)
> Mocha + Chai
  Jest

기본 기능 테스트

src/sum.js

let sum = (a, b) => a + b;
export default sum;

tests/unit/sum.spec.js

import { expect } from 'chai';
import sum from '@/sum';

describe('sum', () => {
    it('add 2+3 equals 5', () => {
        expect(sum(2, 3)).to.equals(5);
    })
});

yarn test:unit 명령 실행

C:\JetBrains\vscode_workspace\test1>yarn test:unit
yarn run v1.22.10
warning ..\package.json: No license field
$ vue-cli-service test:unit
Download the Vue Devtools extension for a better development experience:
https://github.com/vuejs/vue-devtools
You are running Vue in development mode.
Make sure to turn on production mode when deploying for production.
See more tips at https://vuejs.org/guide/deployment.html
 WEBPACK  Compiling...

  [=========================] 98% (after emitting)

 DONE  Compiled successfully in 5362ms

  [=========================] 100% (completed)

 WEBPACK  Compiled successfully in 5362ms

 MOCHA  Testing...



  HelloWorld.vue
    √ renders props.msg when passed (39ms)

  sum
    √ add 2+3 equals 5


  2 passing (46ms)

 MOCHA  Tests completed successfully

Done in 13.85s.

packge.json

{
  "name": "test1",
  "version": "0.1.0",
  "private": true,
  "scripts": {
.....
    "test:unit": "vue-cli-service test:unit",
....
  },
  "dependencies": {
    "core-js": "^3.6.5",
    "vue": "^2.6.11"
  },
  "devDependencies": {
....
    "@vue/cli-plugin-unit-mocha": "~4.5.0",
....
    "@vue/test-utils": "^1.0.3",
    "babel-eslint": "^10.1.0",
    "chai": "^4.1.2",
    "eslint": "^6.7.2",
....
  },
  ....
}

test/unit/example.spec.js

  • describe : 관련 테스트들을 묶어 테스트 블록을 테스트 스위트 형태로 생성
  • it : test 함수의 별칭
  • ShallowMount : @vue/test-utils 패키지에서 제공되고, HellowWorld 컴포넌트에 propsdta 의 msg 값을 마운트
    • div.hello>h1{{{msg}}} 값이 msg 변수값과 일치하는지 비교
    • mount 함수와의 차이점 : mount는 전체 자식 컴포넌트까지 랜더링 하지만 shallowMount는 자식 컴포넌트를 랜더링 하지 않고 직접 마운트 된 컴포넌트만 렌더링
  • text : HTML 태그를 제외한 문자열만 추출
import { expect } from 'chai'
import { shallowMount } from '@vue/test-utils'
import HelloWorld from '@/components/HelloWorld.vue'

describe('HelloWorld.vue', () => {

  it('renders props.msg when passed', () => {
    const msg = 'new message'
    /*
<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
....
  </div>
</template>    
    */
    const wrapper = shallowMount(HelloWorld, {
      propsData: { msg }
    })
    expect(wrapper.text()).to.include(msg)
  })
})

Vue 컴포넌트 테스트

totolistapp 프로젝트 이용

Mocha 테스트 솔루션 추가

C:\JetBrains\vscode_workspace\test1>vue add @vue/unit-mocha
 WARN  There are uncommitted changes in the current repository, it's recommended to commit or stash them first.
? Still proceed? Yes

📦  Installing @vue/cli-plugin-unit-mocha...

yarn add v1.22.10
[1/4] Resolving packages...
[2/4] Fetching packages...
info fsevents@2.1.3: The platform "win32" is incompatible with this module.
info "fsevents@2.1.3" is an optional dependency and failed compatibility check. Excluding it from installation.
info fsevents@1.2.13: The platform "win32" is incompatible with this module.
info "fsevents@1.2.13" is an optional dependency and failed compatibility check. Excluding it from installation.
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Saved 0 new dependencies.
Done in 12.59s.
✔  Successfully installed plugin: @vue/cli-plugin-unit-mocha


🚀  Invoking generator for @vue/cli-plugin-unit-mocha...
⚓  Running completion hooks...

✔  Successfully invoked generator for plugin: @vue/cli-plugin-unit-mocha

example.spec.js 파일 삭제

test/unit/List.spec.js

  • Vue.nextTick : 비동기로 처리.
    • 다음 DOM 업데이트 사이클이 완료된 후에 테스트가 수행될 수 있도록 Promise 패턴을 사용해 then 내부에서 테스트 수행
    • 마지막 done 인자를 사용해 테스트가 완료 되었음을 알림
    • before : 테스트의 시작 전 실행
    • after : 테스트 종료 후 실행
    • beforeEach : 테스트 스위트 전체의 시작 전 실행
    • afterEach : 테스트 스위트 전체의 종료 후 실행
  • dispatchEvent : 클릭 이벤트를 일으킴
  • _watcher.run : Vue 인스턴스에서 상태의 변경을 감지할 수 있도록 감시자를 구동
import { expect } from 'chai';
import { mount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex';
import Vue from 'vue';
import store from '@/store';
import List from '@/components/List.vue';
import Constant from '@/Constant';

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

describe('List.vue', () => {

    let wrapper;

    before(() => {
        wrapper = mount(List, { store, localVue });
    })

    it('초기 렌더링 화면 테스트', (done) => {
        //const wrapper = mount(List, { store, localVue });
        Vue.nextTick().then(() => {
            expect(wrapper.vm.$el.querySelectorAll('li').length).to.equal(4);
            done();
        }).catch(done);
    })

    it('새로운 todo 추가 후 목록확인', (done) => {
        wrapper.vm.$store.dispatch(Constant.ADD_TODO, { todo: "스쿼시" });
        wrapper.vm.$store.dispatch(Constant.ADD_TODO, { todo: "수영" });

        Vue.nextTick().then(() => {
            var list = wrapper.vm.$el.querySelectorAll('li');
            expect(list[list.length - 1].textContent).to.contain('수영');
            expect(list[list.length - 2].textContent).to.contain('스쿼시');
            expect(list.length).to.equal(6);
            done();
        }).catch(done);
    });

    it('클릭 이벤트 후 렌더링 결과 확인', (done) => {
        /* 
        입력값을 TodoList의 첫번째 자식 컴포넌트인
        InputTodo 컴포넌트의 로컬 데이터에 새로운 todo 입력
        */
        wrapper.vm.$children[0].data.todo = "피겨 강습";

        const evtClick = new Window.Event("click");
        var addbutton = wrapper.vm.$el.querySelector('span.addbutton');
        addbutton.dispatchEvent(evtClick);
        wrapper.vm._watcher.run();

        Vue.nextTick().then(() => {
            var list = wrapper.vm.$el.querySelectorAll('li');
            expect(list[list.length - 1].textContent).to.contain('피겨 강습');
            expect(list.length).to.equal(5);
            done();
        }).catch(done);
    });
});

Jest를 이용한 테스트 방법

Facebook에서 만든 테스트 솔루션으로 다음과 같은 장점이 있음

  • 쉬운 설정 : 사용자의 zero-configuration 경험을 제공하는것이 Jest의 기본 철학이라고 밝힐 만큼 설정 파일의 작성을 최소화
  • 스냅샷 테스트 : DOM 요소의 스냅샷을 캡처해서 저장하고 다음 번 테스트 시에 기존 스냅샷과 비교할 수있는 스냅샷 테스트 기능을 손쉽게 사용 가능

Jest 설정

yarn install
vue add @vue/unit-jest

__tests__/sum.spec.js

import sum from '@/sum';

describe('sum 테스트', function() {
    it('add 2+3 equals 5', () => {
        expect(sum(2, 3)).toBe(5);
    })
});

Vue 컴포넌트에 대한 테스트

__tests__/LIst.spec.js

import { mount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex';
import Vue from 'vue';
import store from '@/store';
import List from '@/components/List.vue';
import Constant from '@/Constant';

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

describe('List.vue', () => {

    let wrapper;

    beforeAll(() => {
        wrapper = mount(List, { store, localVue });
        wrapper.vm.$store.dispatch(Constant.ADD_TODO, { todo: "스쿼시" });
    })

    it('초기 렌더링 화면 테스트(Jest)', () => {
        expect(wrapper.vm.$el.querySelectorAll('li').length).toEqual(5);
    })

    it('스냅샷 테스트(Jest)', (done) => {
        Vue.nextTick().then(() => {
            expect(wrapper.vm.$el).toMatchSnapshot();
            done();
        }).catch(done);
    });
});

jest 실행

yarn test:unit
또는

설치후
npm install -g npx
npx jest (프로젝트 디렉터리에서)

스냅샷을 갱신하고 싶다면
npx jest -u (프로젝트 디렉터리에서)

' > Vue.js 퀵스타트' 카테고리의 다른 글

서버 사이드 렌더링  (0) 2020.11.28
트랜지션 효과  (0) 2020.11.28
vue-router를 이용한 라우팅  (0) 2020.11.28
Vuex를 이용한 상태관리  (0) 2020.11.27
axios를 이용한 서버통신  (0) 2020.11.25