Vue에서 단위 테스트하기

GwangJae Han·2022년 2월 28일
0
post-thumbnail

테스트

바닐라 자바스크립트 그리고 Vue나 React와 같은 SPA를 포함한 모든 프론트엔드 개발에서 직접 인터렉션하며 페이지가 이상이 없는지 테스트하게 됩니다. 이러한 동작은 매우 반복적으로 수행하게 되어 불필요한 시간을 낭비하게 되지만, 이렇게 테스트한 것들은 이후에 새로운 기능이 추가된 버전이 반영되었을 때도 제대로 동작하는지 확인하기 위해 또다시 반복합니다. 이런 반복적인 동작을 줄이기 위해 테스트를 자동화하는 작업이 필요하며 이를 통해 필요한 테스트가 빠지거나, 오류를 확인 못 하는 문제를 방지할 수 있어 코드 품질과 신뢰성을 올릴 수 있으므로 테스트합니다.

물론, 프로젝트의 규모나 상황에 따라 테스트하지 못하게 되는 상황이 있을 수 있습니다. 따라서 테스트는 무조건 하는 것이 아닌, 필요 때문에 한다는 것에 주의하시면 좋겠습니다.

단위 테스트 (Unit Test)

단위 테스트 또는 유닛 테스트는 코드가 제대로 작동하는지 확인하기 위해 모듈별로 독립적으로 테스트하는 것을 의미합니다. Vue에서 단위 테스트를 해야 할 것은 컴포넌트이며 아래와 같은 정보들을 확인합니다.

  • 요구사항에 작성된 요소가 렌더링되는지
  • 해당 요소에 적절한 값이 보여지는지
  • 요구사항에 작성된 기능이 동작되는지

Vue는 Vue Test Utils라는 문서를 이용해 단위 테스트하는 방법을 익힐 수 있습니다. 해당 문서에서 언급된 테스트 코드를 쉽게 작성하기 위해 제안하는 것은 컴포넌트의 세부 정보를 테스트하지 말 것, 가능한 한 단순한 컴포넌트로 구축할 것 그리고 컴포넌트를 작성하기 전에 테스트 코드를 작성할 것입니다.

컴포넌트의 세부 정보를 테스트하지 말 것

컴포넌트에서 세부 정보를 이용해 테스트할 경우, 해당 컴포넌트의 세부 구현이 바뀌게 되면 대응되는 테스트 코드도 변경해야 합니다. 즉, 의존성이 생기게 됩니다. 이런 부분이 생기게 되는 경우는 컴포넌트의 입력과 출력을 확인하기 위해서입니다. 예를 들어 input 태그에 특정 글자를 입력한 후, 그 값이 제대로 입력되었는지 확인하고 싶을 때 input에 bind한 data를 이용해 직접 확인할 수 있겠으나 만약 이름이 바뀌게 되었을 때 수정을 해야 하는 문제가 있습니다. vue-test-utils에서 말하는 입력과 출력은 아래와 같이 있고 이 외의 모든 것은 세부 정보에 해당합니다.

입력
interaction클릭, 타이핑과 같은 사람의 상호작용
Props컴포넌트가 받는 인수
Data streamsAPI 호출에서 들어오는 데이터

출력
DOM elements문서에 렌더링된 모든 관찰 가능한 노드
Event$emit을 사용했을 때 방출된 이벤트
Side Effectsconsole.log 또는 API 호출과 같은 것

예를 들어 카운터를 증가시키는 버튼이 있는 기본 Counter 컴포넌트가 있을 때

<template>
  <p class="paragraph">Times clicked: {{ count }}</p>
  <button @click="increment">increment</button>
</template>

<script>
export default {
  data() {
    return { count: 0 }
  },
  methods: {
    increment() {
      this.count++
    }
  }
}
</script>

다음과 같은 테스트를 작성할 수 있습니다.

import { mount } from '@vue/test-utils'
import Counter from './Counter.vue'

test('counter text updates', async () => {
  const wrapper = mount(Counter)
  const paragraph = wrapper.find('.paragraph')

  expect(paragraph.text()).toBe('Times clicked: 0')

  await wrapper.setData({ count: 2 })

  expect(paragraph.text()).toBe('Times clicked: 2')
})

얼핏 보면 문제가 없어 보이지만, 사용자의 interaction에 값이 변한 것이 아닌 직접 count를 변경했습니다. 그리고 CSS의 paragraph의 클래스 식별자를 이용해 적절한 문장이 표시되는지 확인했기 때문에 만약 해당 클래스 식별자가 바뀌게 되면 테스트 코드를 수정해야 합니다. 따라서, 아래와 같이 코드를 작성해 위에서 기술한 문제점들을 해결할 수 있습니다.

import { mount } from '@vue/test-utils'

test('text updates on clicking', async () => {
  const wrapper = mount(Counter)

  expect(wrapper.text()).toContain('Times clicked: 0')

  const button = wrapper.find('button')
  await button.trigger('click')
  await button.trigger('click')

  expect(wrapper.text()).toContain('Times clicked: 2')
})

해당 컴포넌트와 테스트 코드는 해당 링크에서 확인할 수 있습니다.

가능한 한 단순한 컴포넌트로 구축할 것

일반적으로 컴포넌트가 작게 구성될수록 테스트를 좀 더 명확하게 할 수 있습니다. 따라서, 컴포넌트를 가능한 한 단순하게 구현하면 읽고 이해하기 쉬운 테스트 코드를 만들 수 있게 됩니다.

컴포넌트를 작성하기 전에 테스트 코드를 작성할 것

단위 테스트를 위한 테스트 코드를 작성하는 것은 테스트 기반 개발인 TDD에 필요한 것입니다. 컴포넌트를 구성하기 전에 테스트 코드를 구현하는 것이 코드의 품질을 향상할 수 있습니다.

Jest

Jest is a delightful JavaScript Testing Framework with a focus on simplicity.
Jest는 단순성에 초점을 맞춘 유쾌한(?) 자바스크립트 테스트 프레임워크입니다.

Vue는 자바스크립트의 프레임워크이기 때문에 Vue의 컴포넌트를 단위 테스트하기 위해선 자바스크립트의 테스트 프레임워크가 필요합니다. 따라서, 자바스크립트 테스트 프레임워크인 Jest를 이용해 Vue 컴포넌트의 단위 테스트를 진행합니다.

Jest의 기본 문법은 아래와 같습니다.

describe

테스트를 묶는 큰 단위입니다. 여러 관련된 테스트들을 묶어 이해하기 쉽게 도와줍니다.

describe('App.vue', () => {
	...
})

it

테스트를 의미합니다. 해당 테스트에 대한 설명과 구현을 합니다.

it('renders p tag', () => {
	...
})

test

it 과 같이 테스트를 의미합니다. it과 차이점은 없으나, it 은 영어로 표현할 때 자연스럽기 때문에 사용합니다. 둘 중 어느 것을 사용해도 무방하나 가급적이면 혼용해서 사용하지 않는 편이 유지보수에 좋다고 생각합니다.

expect

테스트의 기댓값을 확인하기 위해 사용합니다. toBetoEqual 과 같은 것으로 평가합니다.

...
expect(3).toBe(3)

toBe

expect 의 인수가 같은지를 확인합니다. 같으면 true , 다르면 false 입니다. toEqual 과 다르게 단순 비교만을 수행합니다.

toEqual

expect 의 인수가 같은지를 확인합니다. 같으면 true , 다르면 false 입니다. toBe 와 다르게 배열이나 객체 내부까지 비교합니다.

toContain

expect 의 인수에 toContain 의 인수가 포함되는지를 확인합니다. 포함되면 true , 그렇지 않으면 false 입니다.

expect('abc').toContain('a') // true
expect('def').toContain('a') // false

beforeEach

테스트를 시작하기 전에 공통된 작업을 처리하는 전처리기입니다. 주로 describe 에 선언해서 그 안에 있는 테스트에 공통 변수들을 초기화하는데 사용됩니다.

describe('Test', () => {
	beforeEach(() => {
		...
	})

	it ('renders p tag', () => {
		...
	})
})

afterEach

테스트가 끝난 후 공통된 작업을 처리하는 후처리기입니다. beforeEach 와 마찬가지로 decribe 에 선언하고 생성한 instance 를 초기화하거나, mocking 한 것이 있으며 해제하는 역할을 합니다.

describe('Test', () => {
	afterEach(() => {
		...
	})

	it ('renders p tag', () => {
		...
	})
})

Vue에서 Jest 사용하기

우선, Vue 프로젝트를 하나 생성합니다. 생성할 때 Unit TestingJest를 체크한다는 점을 주의하시면 됩니다.

  1. vue-cli를 이용해 프로젝트 생성

  2. Manually select features를 선택해서 아래와 같이 Unit Testing을 체크합니다.

  3. Pick a unit testing solutionJest를 선택한 후 프로젝트를 생성합니다.

프로젝트를 생성한 후 IDE를 통해 코드를 확인해보면, tests/unitexample.spec.js가 있는 것을 확인할 수 있습니다. 해당 파일은 HelloWorld.vue를 단위 테스트하기 위한 코드가 있습니다.

이 과정으로 통해 Vue에서 단위 테스트를 할 수 있는 환경이 갖춰졌습니다.

profile
안녕하세요! 프론트엔드 개발자 한광재입니다.

0개의 댓글