티스토리 뷰
728x90
코드가 변경되면 자동으로 테스트가 수행되도록 스크립트를 하나 추가했습니다.
// package.json
"scripts": {
"test": "jest",
"test:watch": "jest --watchAll"
},
그리고 layouts 폴더도 테스트 커버리지에 포함시켰습니다.
// jest.config.js
collectCoverageFrom: [
'<rootDir>/components/**/*.vue',
'<rootDir>/pages/**/*.vue',
'<rootDir>/layouts/**/*.vue'
]
테스트 코드를 작성하면서 만났던 이슈는 체크 박스의 checked 값을 확인할 수 없다는 사실이었습니다.
그래서 체크 박스에 vlaue 값을 추가했고 그 값으로 렌더링이 정상적으로 되었는지 체크했습니다.
// TodoList.vue
// before
<input :checked="todo.completed" type="checkbox" @change="toggleTodo(todo.id)">
// after
<input :value="todo.completed" :checked="todo.completed" type="checkbox" @change="toggleTodo(todo.id)">
테스트 코드도 코드이기 때문에 중복되는 코드는 함수로 분리(getStore, getWrapper)해서 재사용했습니다.
BDD 방식으로 단위 테스트를 작성했습니다. 보기 편하게 하려고 given-when-then 주석을 달았습니다.
import { createLocalVue, shallowMount, RouterLinkStub } from '@vue/test-utils'
import Vuex from 'vuex'
import { getters } from '~/store'
import TodoList from '~/components/TodoList'
const localVue = createLocalVue()
localVue.use(Vuex)
describe('TodoList Component', () => {
const getStore = (state, actions) => {
return new Vuex.Store({
state,
getters,
actions
})
}
const getWrapper = (store) => {
return shallowMount(TodoList, {
store,
localVue,
stubs: {
NuxtLink: RouterLinkStub
}
})
}
test('does not render li when store state todos is empty', () => {
// given
const state = {
todos: [],
visibility: 'all',
selectedTodo: null
}
const store = getStore(state)
// when
const wrapper = getWrapper(store)
// then
expect(wrapper.findAll('li').length).toBe(0)
})
test('render todo when store state todos is not empty', () => {
// given
const todo = {
id: 'todo-1',
text: 'aadfjadfasdfasdfasdf',
completed: false
}
const state = {
todos: [todo],
visibility: 'all',
selectedTodo: null
}
const store = getStore(state)
// when
const wrapper = getWrapper(store)
// then
expect(wrapper.findAll('li').length).toBe(1)
expect(wrapper.find('li').find('span').text()).toBe(todo.text)
})
test('completed checkbox\'s value false when store todo completed value is false.', () => {
// given
const state = {
todos: [{
id: 'todo-1',
text: 'aadfjadfasdfasdfasdf',
completed: false
}],
visibility: 'all',
selectedTodo: null
}
const store = getStore(state)
// when
const wrapper = getWrapper(store)
// then
expect(wrapper.find('input[type="checkbox"]').attributes('value')).toBe('false')
})
test('completed checkbox\'s value true when store todo completed value is true.', () => {
// given
const state = {
todos: [{
id: 'todo-1',
text: 'aadfjadfasdfasdfasdf',
completed: true
}],
visibility: 'all',
selectedTodo: null
}
const store = getStore(state)
// when
const wrapper = getWrapper(store)
// then
expect(wrapper.find('input[type="checkbox"]').attributes('value')).toBe('true')
})
test('call store action `toggleTodo` when completed checkbox is clicked', () => {
// given
const toggleTodo = jest.fn()
const state = {
todos: [{
id: 'todo-1',
text: 'aadfjadfasdfasdfasdf',
completed: false
}],
visibility: 'all',
selectedTodo: null
}
const store = getStore(state, {
toggleTodo
})
const wrapper = getWrapper(store)
// when
wrapper.find('input[type="checkbox"]').trigger('click')
// then
expect(toggleTodo).toHaveBeenCalledTimes(1)
})
test('call store action `setTodo` when todo is clicked', () => {
// given
const setTodo = jest.fn()
const state = {
todos: [{
id: 'todo-1',
text: 'aadfjadfasdfasdfasdf',
completed: false
}],
visibility: 'all',
selectedTodo: null
}
const store = getStore(state, {
setTodo
})
const wrapper = getWrapper(store)
// when
wrapper.find('span').trigger('click')
// then
expect(setTodo).toHaveBeenCalledTimes(1)
})
test('call store action `setTodo` when edit button is clicked ', () => {
// given
const setTodo = jest.fn()
const state = {
todos: [{
id: 'todo-1',
text: 'aadfjadfasdfasdfasdf',
completed: false
}],
visibility: 'all',
selectedTodo: null
}
const store = getStore(state, {
setTodo
})
const wrapper = getWrapper(store)
// when
wrapper.findAll('button').at(1).trigger('click')
// then
expect(setTodo).toHaveBeenCalledTimes(1)
})
test('call store action `removeTodo` when delete button is clicked', () => {
// given
const removeTodo = jest.fn()
const state = {
todos: [{
id: 'todo-1',
text: 'aadfjadfasdfasdfasdf',
completed: false
}],
visibility: 'all',
selectedTodo: null
}
const store = getStore(state, {
removeTodo
})
const wrapper = getWrapper(store)
// when
wrapper.findAll('button').at(0).trigger('click')
// then
expect(removeTodo).toHaveBeenCalledTimes(1)
})
})
전체 코드는 github에 있습니다.
# 추가
# 2020. 5. 8
trigger 후 render 결과를 확인하려는 경우 이슈가 있습니다.
공식문서에 async/await 방법을 아래 처럼 가이드 하고 있으나 beta.33에서는 동작하지 않습니다.
it('button click should increment the count text', async () => {
expect(wrapper.text()).toContain('0')
const button = wrapper.find('button')
await button.trigger('click')
expect(wrapper.text()).toContain('1')
})
https://github.com/vuejs/vue-test-utils/issues/1515 에서 추가로 수정되었는데 아직 release가 안됐습니다.
당장 해결 방법은 async/await를 사용하지 않고 promise 패턴으로 사용하는 방식으로 아래처럼 가능합니다.
it('button click should increment the count text', () => {
expect(wrapper.text()).toContain('0')
const button = wrapper.find('button')
button.trigger('click').then(() => {
expect(wrapper.text()).toContain('1')
})
})
# 참고
728x90
댓글