Vue + Jest

MAX GUNยท2021๋…„ 7์›” 6์ผ
1
post-thumbnail

๐Ÿ›ธVue + Jest

์‚ฌ์ด๋“œ ํ”„๋กœ์ ํŠธ๋ฅผ ์‹ค์‹œํ•˜๋‹ค๊ฐ€ ํ…Œ์ŠคํŠธ์ฝ”๋“œ์— ๊ด€์‹ฌ์„ ๊ฐ–๊ฒŒ ๋˜์—ˆ๋‹ค.
Vue์—์„œ๋Š” Jest ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์ถ”์ฒœํ•˜์˜€๋‹ค. ๊ทธ๋ž˜์„œ ๋‚˜์˜ ์‚ฌ์ด๋“œ ํ”„๋กœ์ ํŠธ์—์„œ
์–ด๋–ป๊ฒŒ ํ…Œ์ŠคํŠธ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ–ˆ๋Š”์ง€ ์ ๊ณ ์ž ํ•œ๋‹ค.

์ฐธ๊ณ ๋กœ Jest ์„ธํŒ…์— ๋Œ€ํ•ด์„œ๋Š” ์ ์ง€๋ชปํ•˜์˜€๋‹ค. ์ถ”ํ›„์— ์ ์„ ์˜ˆ์ •์ด๋‹ค.

๐Ÿš€ ํ”„๋กœ์ ํŠธ ์Šคํƒ

Vuetify,Vue,Jest

๐ŸšBoard๊ตฌ์กฐ

"Board" ์ปดํผ๋„ŒํŠธ ๊ตฌ์กฐ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

  • Board
    • BoardCreateForm.vue
    • BoardEditForm.vue
    • BoardList.vue
    • BoardReadForm.vue
    • BoardSearchBox.vue

์—ฌ๊ธฐ์„œ ๋‘๊ฐ€์ง€ ์ปดํผ๋„ŒํŠธ๋ฅผ ํ…Œ์ŠคํŠธ ํ•˜๊ณ ์ž ํ•œ๋‹ค. "BoardSearchBox.vue", "BoardList.vue" ๊ฐ€ ํ…Œ์ŠคํŠธํ•  ๋Œ€์ƒ์ด๋‹ค.

Board ํ™”๋ฉด


Board ์‹œ๋‚˜๋ฆฌ์˜ค



์ „์ฒด ์ฝ”๋“œ

import Vuetify from 'vuetify';
import Vuex from 'vuex';
import VueRouter from 'vue-router';
import { shallowMount, createLocalVue, mount } from '@vue/test-utils';
import BoardSearchBox from 'root/components/board/BoardSearchBox';
import Board from 'root/view/board/Board';
import BoardCreate from 'root/view/board/BoardCreate';
import { boardStoreMock } from 'root/components/board/tests/mockStore/boardStoreMock';
import routes from 'root/routes/routes';
const localVue = createLocalVue();
localVue.use(Vuex);
localVue.use(VueRouter);
const store = new Vuex.Store({
	modules: {
		boardStore: boardStoreMock,
	},
});

describe('BoardSearchBox ํ…Œ์ŠคํŠธ', () => {
	let vuetify;
	let wrapper;
	let boardWrapper;
	let router;
	beforeEach(() => {
		vuetify = new Vuetify();
		router = new VueRouter({ routes });
		wrapper = shallowMount(BoardSearchBox, {
			vuetify,
			localVue,
			store,
		});
		boardWrapper = mount(Board, {
			localVue,
			router,
		});
	});

	test('์ œ๋ชฉ์„ ์ž…๋ ฅํ•˜๋ฉด id๊ฐ€ title์ธ ๊ฐ’์ด ๋ณ€๊ฒฝ๋œ๋‹ค.', () => {
		wrapper.find('#title').vm.$emit('input', 'CHOI');
		expect(wrapper.find('#title')._emittedByOrder[0].args[0]).toBe('CHOI');
	});

	test('์ž‘์„ฑ์ž๋ฅผ ์ž…๋ ฅํ•˜๋ฉด id๊ฐ€ writer์ธ ๊ฐ’์ด ๋ณ€๊ฒฝ๋œ๋‹ค.', () => {
		wrapper.find('#writer').vm.$emit('input', '์ตœ๋Œ€๊ฑด');
		expect(wrapper.find('#writer')._emittedByOrder[0].args[0]).toBe('์ตœ๋Œ€๊ฑด');
	});

	test('์ œ๋ชฉ๊ณผ ์ž‘์„ฑ์ž๋ฅผ ์ž…๋ ฅ ํ›„ BoardStore์—์„œ AC_BOARD_LIST๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.', async () => {
		wrapper.find('#searchBtn').vm.$emit('click');
		await wrapper.vm.$nextTick();
		expect(boardStoreMock.actions.AC_BOARD_LIST).toHaveBeenCalled();
	});

	test('์‹ ๊ทœ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด BoardCreate๋กœ ์ด๋™ํ•œ๋‹ค', async () => {
		wrapper.find('#createBoard').vm.$emit('click');
		router.push('/main/board/create');
		await boardWrapper.vm.$nextTick();
		expect(boardWrapper.findComponent(BoardCreate).exists()).toBe(true);
	});
});

๐ŸŒ 1.์ œ๋ชฉ์„ ์ž…๋ ฅํ•ด์„๋•Œ ์ œ๋ชฉ์˜ ๊ฐ’๊ณผ v-text-field์˜ ๊ฐ’์ด ์ผ์น˜ํ•˜๋Š”์ง€ ํ™•์ธํ•œ๋‹ค.

๋จผ์ € "BoardSearchBox"๋งŒ ํ…Œ์ŠคํŠธ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— Vue์— ํ•„์š”ํ•œ ๊ฒƒ๋“ค์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด "createLocalVue"๋ฅผ ์‚ฌ์šฉํ•˜์˜€๋‹ค.
์—ฌ๊ธฐ์„œ ์„ธํŒ…ํ•˜๋Š” ๋ถ€๋ถ„์„ ์„ค๋ช…ํ•˜๋ฉด ๋‚ด์šฉ์ด ๊ธธ์–ด์ง€๋ฏ€๋กœ ์ƒ๋žต ํ•˜๊ฒ ๋‹ค.
"test(('ํ…Œ์ŠคํŠธ ํ•  ๋‚ด์šฉ')=>{ ํ…Œ์ŠคํŠธ ๋กœ์ง })"

์—ฌ๊ธฐ์„œ ์ค‘์š” ํฌ์ธํŠธ๋Š” ํ™”๋ฉด์— ์ž…๋ ฅํ–ˆ์„ ๋•Œ v-text-field ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๊ฒƒ์ด๋‹ค. test์ฝ”๋“œ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

test('์ œ๋ชฉ์„ ์ž…๋ ฅํ•˜๋ฉด id๊ฐ€ title์ธ ๊ฐ’์ด ๋ณ€๊ฒฝ๋œ๋‹ค.', () => {
		wrapper.find('#title').vm.$emit('input', 'CHOI');
		expect(wrapper.find('#title')._emittedByOrder[0].args[0]).toBe('CHOI');
});

2.๐Ÿฆผ์ž‘์„ฑ์ž๋ฅผ ์ž…๋ ฅํ•˜๋ฉด id๊ฐ€ writer์ธ ๊ฐ’์ด ๋ณ€๊ฒฝ๋œ๋‹ค.

1๋ฒˆ๊ณผ ๋Œ์•„๊ฐ€๋Š” ๋กœ์ง์€ ๊ฐ™๋‹ค.

test('์ž‘์„ฑ์ž๋ฅผ ์ž…๋ ฅํ•˜๋ฉด id๊ฐ€ writer์ธ ๊ฐ’์ด ๋ณ€๊ฒฝ๋œ๋‹ค.', () => {
		wrapper.find('#writer').vm.$emit('input', 'ํ™๊ธธ๋™');
		expect(wrapper.find('#writer')._emittedByOrder[0].args[0]).toBe('ํ™๊ธธ๋™');
});

3.๐Ÿ•Œ์ œ๋ชฉ๊ณผ ์ž‘์„ฑ์ž๋ฅผ ์ž…๋ ฅ ํ›„ BoardStore์—์„œ AC_BOARD_LIST๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.

์—ฌ๊ธฐ์„œ๋Š” Vuex์˜ BoardStore๋ชจ๋“ˆ ์•ˆ์— AC_BOARD_LIST๋ฅผ ํ˜ธ์ถœํ•ด์•ผ ํ•œ๋‹ค.
ํ•˜์ง€๋งŒ AC_BOARD_LIST๋ฅผ ๋ฐฑ์—”๋“œ์™€์˜ ํ†ต์‹ ์ด ํ•„์š”ํ•œ ์ž‘์—…์ด๋‹ค. ํ•˜์ง€๋งŒ ์—ฌ๊ธฐ์„œ๋Š” AC_BOARD_LIST๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•˜๋‹ค. ๊ทธ๋ž˜์„œ mock์„ ํ™œ์šฉํ•˜์˜€๋‹ค.

boardStoreMock ๋งŒ๋“ค์—ˆ์œผ๋ฉฐ ์ด๊ฒƒ์€ ๊ฐ€์งœ BoardStore ๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค.

//boardStoreMock.js
const state = {
	search: {
		title: '',
		writer: '',
		page: 1,
		itemPerPage: 5,
	},
};
const boardStoreMock = {
	state,
	getters: {
		GE_SEARCH() {
			return state.search;
		},
	},
	mutations: {
		MU_SEARCH: jest.fn(),
	},
	actions: {
		AC_BOARD_LIST: jest.fn(),
	},
	namespaced: true,
};
export { boardStoreMock };

์•„๋ž˜ ์ฝ”๋“œ๊ฐ€ AC_BOARD_LIST๋ฅผ ํ˜ธ์ถœํ–ˆ๋Š”์ง€ ํ™•์ธํ•˜๋Š” ์ฝ”๋“œ์ด๋‹ค.

test('์ œ๋ชฉ๊ณผ ์ž‘์„ฑ์ž๋ฅผ ์ž…๋ ฅ ํ›„ BoardStore์—์„œ AC_BOARD_LIST๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.', async () => {
		wrapper.find('#searchBtn').vm.$emit('click');
		await wrapper.vm.$nextTick();
		expect(boardStoreMock.actions.AC_BOARD_LIST).toHaveBeenCalled();
});

4.โ›ฝ์ œ๋ชฉ๊ณผ ์ž‘์„ฑ์ž๋ฅผ ์ž…๋ ฅ ํ›„ BoardStore์—์„œ AC_BOARD_LIST๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.

์—ฌ๊ธฐ์„œ๋Š” ์‹ ๊ทœ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ํ™”๋ฉด์ด ๋ฐ”๋€Œ์–ด์•ผ ํ•œ๋‹ค. ์ด ๋ง์„ ํ‘œํ˜„ํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™๋‹ค.
BoardList => BoardCreate๋กœ router-view๊ฐ€ ๋ฐ”๋€Œ์–ด์•ผ ํ•œ๋‹ค.

๊ทธ๋Ÿฌ๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋‘๊ฐ€์ง€๊ฐ€ ํ•„์š”ํ•˜๋‹ค. Board.vue,BoardCreate.vue
๊ทธ๋ฆฌ๊ณ  ์—ฌ๊ธฐ์„œ์˜ "ํฌ์ธํŠธ"๋Š” BoardCreate๊ฐ€ ํ™”๋ฉด์ด ์กด์žฌํ•˜๋Š”์ง€๋งŒ ํŒ๋‹จํ•˜๋ฉด ๋œ๋‹ค.

4-1. Board.vue,BoardCreate.vue ๋ฅผ importํ•˜์—ฌ์•ผ ํ•œ๋‹ค.

.
.
.
import Board from 'root/view/board/Board';
import BoardCreate from 'root/view/board/BoardCreate';

4-2. vue-router์™€ localVue๊ฐ€ vue-router๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ค€๋‹ค.

.
.
.
import VueRouter from 'vue-router';
.
.
.
const localVue = createLocalVue();
localVue.use(VueRouter);

4-3. ๋ณธ์ธ ํ”„๋กœ์ ํŠธ์— ๊ด€๋ฆฌํ•˜๊ณ  ์žˆ๋Š” router๋ฅผ importํ•ด์ฃผ๊ณ  beforeEach๋ถ€๋ถ„์—์„œ board์— router๋ฅผ ๋“ฑ๋กํ•ด์ค€๋‹ค.

import routes from 'root/routes/routes';
.
.
.
describe('BoardSearchBox ํ…Œ์ŠคํŠธ', () => {
.
.
let router;
let boardWrapper;
  beforeEach(() => {
        router = new VueRouter({ routes });
    		wrapper = shallowMount(BoardSearchBox, {
			router,
		});
		boardWrapper = mount(Board, {
			router,
		});
	});
})

4-4. ์—ฌ๊ธฐ์„œ๋Š” BoardCreate ์ปดํผ๋„ŒํŠธ๊ฐ€ ์กด์žฌํ•˜๋Š”์ง€ ํ™•์ธํ•˜๋Š”๊ฒŒ ์ค‘์š”ํ•˜๋ฏ€๋กœ BoardCreate๋ฅผ mock์œผ๋กœ ๋งŒ๋“ค์–ด์ค€๋‹ค.

jest.mock('root/view/board/BoardCreate', () => ({
	name: 'boardCreate',
	render: h => h('div'),
}));

describe('BoardSearchBox ํ…Œ์ŠคํŠธ', () => {
    .
    .
    .
    .

})

4-5. testํ•จ์ˆ˜์— ๋กœ์ง์„ ์ž‘์„ฑํ•ด์ค€๋‹ค. ๋กœ์ง์€ "createBoard"๋ผ๋Š” id๋ฅผ ๊ฐ€์ง„ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•œ๋‹ค. ํด๋ฆญ ํ›„์—๋Š” router๋ฅผ ํ˜ธ์ถœ ํ•˜๊ณ  boardWrapper๋ฐ‘์— BoardCreate๊ฐ€ ์กด์žฌํ•˜๋Š”์ง€ ํ™•์ธํ•œ๋‹ค.

test('์‹ ๊ทœ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด BoardCreate๋กœ ์ด๋™ํ•œ๋‹ค', async () => {
		wrapper.find('#createBoard').vm.$emit('click');
		router.push('/main/board/create');
		await boardWrapper.vm.$nextTick();
		expect(boardWrapper.findComponent(BoardCreate).exists()).toBe(true);
});

๐ŸŒž ํ›„๊ธฐ

์•„์ง๊นŒ์ง€ ๋ถ€์กฑํ•œ ๋ถ€๋ถ„๋„ ๋งŽ๊ณ  ๋ณด์™„ํ•  ๋ถ€๋ถ„๋„ ๋งŽ์œผ๋ฉฐ ๋‹ค์Œ์—๋Š” BoardList ์ปดํผ๋„ŒํŠธ๋ฅผ ํ…Œ์ŠคํŠธํ•˜๊ณ ์ž ํ•œ๋‹ค.

profile
์„ฑ์žฅํ•˜๋Š” ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž

0๊ฐœ์˜ ๋Œ“๊ธ€