์ํ ๊ด๋ฆฌ ๋ชจํนํ๊ธฐ
์ํ state : ์๊ฐ์ ๋ฐ๋ผ ๋ณํ ์ ์๋ ๋ฐ์ดํฐ. ํ๋ก ํธ์์ ํนํ '์ํ'๊ฐ ์ค์ํ๊ฒ ๋ค๋ค์ง๋ ์ด์ ๋ UI๊ฐ ์ํ์ ์ง์ ์ ์ผ๋ก ์์กดํ๊ธฐ ๋๋ฌธ์ด๋ค.
๊ฐ์ ์์ ์์ CartTable ์ปดํฌ๋ํธ๋ ๋ค์๊ณผ ๊ฐ์ ํ์ ์ปดํฌ๋ํธ๋ค๋ก ๊ตฌ์ฑ๋์ด ์๋ค.
const CartTable = () => {
return (
<>
<PageTitle />
<ProductInfoTable />
<Divider sx={{ padding: 2 }} />
<PriceSummary />
</>
);
};
์ฌ๊ธฐ์ PageTitle๊ณผ Divider ์ปดํฌ๋ํธ๋ ๋๋ฌด ๋จ์ํ๊ธฐ ๋๋ฌธ์ ํ
์คํธ๊ฐ ํ์ํ์ง ์๊ณ , ๋ค์ ๋ ๊ฐ์ ์ปดํฌ๋ํธ๋ ํ
์คํธ๊ฐ ํ์ํ๋ค :
ProductInfoTable: ์ฅ๋ฐ๊ตฌ๋์ ๋ด๊ธด ์ํ ๋ชฉ๋กPriceSummary: ์ฅ๋ฐ๊ตฌ๋์ ๋ด๊ธด ์ํ๋ค์ ๊ฐ๊ฒฉ ์ดํฉ
์ ๋ ์ปดํฌ๋ํธ์ ๋ํด ๊ฐ๊ฐ ํตํฉ ํ
์คํธ๋ฅผ ์์ฑํด์ผ ํ๋ค. ์ CartTable ์ปดํฌ๋ํธ์ ๋ก์ง์ ์์ง์์ผ ํ
์คํธ๋ฅผ ํ๋๋ก ํตํฉํ์ง ์๊ณ ๊ตณ์ด ๋๊ฐ๋ก ๋๋ด์๊น?
zustand ๋ชจํน
์ผ๋จ ๋ ์ปดํฌ๋ํธ๋ ๋ชจ๋ zustand ์คํ ์ด์์ ์ํ๋ ์ก์ ์ ์ฌ์ฉํ๋ค.๊ทธ๋์ ์ด ์ํ์ ์ก์ ์ ๋ชจํนํ๋ค๋ฉด ๋ ์ปดํฌ๋ํธ ๋ชจ๋ ํ ์คํธํ ์ ์๊ฒ ๋๋ค.
์ฃผ๋ก ๋ฃจํธ์ __mocks__ ํด๋๋ฅผ ๋ง๋ค๊ณ ํ์ ๊ฒฝ๋ก์ ๋ชจํนํ์ผ์ ์์ฑํด๋๋ฉด vitest๋ jest ์์ ์๋์ผ๋ก ๋ชจํน์์ ์ฌ์ฉํ๋ค. zustand ๋ฌธ์
// __mocks__/zustand.js
const { create: actualCreate } = await vi.importActual('zustand');
import { act } from '@testing-library/react';
// ์ฑ์ ์ ์ธ๋ ๋ชจ๋ ์คํ ์ด์ ๋ํด ์ฌ์ค์ ํจ์๋ฅผ ์ ์ฅ
const storeResetFns = new Set();
// ์คํ ์ด๋ฅผ ์์ฑํ ๋ ์ด๊ธฐ ์ํ๋ฅผ ๊ฐ์ ธ์ ๋ฆฌ์
ํจ์๋ฅผ ์์ฑํ๊ณ set์ ์ถ๊ฐํฉ๋๋ค.
export const create = createState => {
const store = actualCreate(createState);
const initialState = store.getState();
storeResetFns.add(() => store.setState(initialState, true));
return store;
};
// ํ
์คํธ๊ฐ ๊ตฌ๋๋๊ธฐ ์ ๋ชจ๋ ์คํ ์ด๋ฅผ ๋ฆฌ์
ํฉ๋๋ค.
// ํ
์คํธ ๋
๋ฆฝ์ฑ ์ ์ง
beforeEach(() => {
act(() => storeResetFns.forEach(resetFn => resetFn()));
});
๊ทธ๋ฆฌ๊ณ ํ
์คํธ ํ๊ฒฝ์ ๊ธ๋ก๋ฒ ์ค์ ์ ํ๋ ์
์
ํ์ผ(์์ ์์๋ setupTests.js)์ ๋ค์๊ณผ ๊ฐ์ด ์์ฑํ๋ค.
import '@testing-library/jest-dom/vitest'
vi.mock('zustand') // to make it work like Jest (auto-mocking)
์ด๋ ๊ฒ ํ๋ฉด ํ
์คํธ ํ๊ฒฝ์์ zustand๋ฅผ ํธ์ถํ ๋์๋ __mocks__/zustand.js์ ์์ฑ๋ ์ฝ๋๊ฐ ํธ์ถ๋๋ค.
๋ชจํน ์ ํธ ํจ์
์์ __mocks__/zustand.js ํ์ผ์ ๋ณด๋ฉด beforeEach๋ก ๋ชจ๋ ํ
์คํธ ์์ ์ ์ ์คํ ์ด๋ฅผ ์ด๊ธฐํ ํ๋ค.
beforeEach(() => {
act(() => storeResetFns.forEach(resetFn => resetFn()));
});
๊ทธ๋ฐ๋ฐ ํ ์คํธ ํ๊ธฐ ์ ์ ํน์ ๋ฐ์ดํฐ๊ฐ ํ์ํ์ง ์์๊น? ์๋ฅผ๋ค์ด ์ฅ๋ฐ๊ตฌ๋์ ์ํ์ด ์์ด์ผ ๊ทธ๊ฒ์ ์ด์ก์ด ์ ๊ณ์ฐ๋๋์ง ๊ฒ์ฆํ๊ฑฐ๋ ์ฅ๋ฐ๊ตฌ๋์์ ์ํ์ ์ ๊ฑฐํ๋ ๋ก์ง๋ ๊ฒ์ฆํ ์ ์์ง ์์๊น?
๊ทธ๋ด๋ ์ฌ์ฉํ๋๊ฒ ์ํ๋ฅผ ์ฃผ์ ํด์ฃผ๋ ์ ํธ ํจ์๋ค.
// src/utils/test/mockZustandStore.jsx
import { useCartStore } from '@/store/cart';
import { useFilterStore } from '@/store/filter';
import { useUserStore } from '@/store/user';
const mockStore = (hook, state) => {
const initStore = hook.getState();
hook.setState({ ...initStore, ...state }, true);
};
export const mockUseUserStore = state => {
mockStore(useUserStore, state);
};
export const mockUseCartStore = state => {
mockStore(useCartStore, state);
};
export const mockUseFilterStore = state => {
mockStore(useFilterStore, state);
};
(์ด ๋ชจํน ์ ํธ ํจ์๋ค์ ๋ณด๋ฉด ๋ฐ๋ก ์ฃผ์คํ ๋ ์คํ ์ด์ ์ํ๋ฅผ ์ง์ ๋ณ๊ฒฝ(hook.setState())ํ๋ ๊ฒ์ ๋ณผ ์ ์๋ค.)
์ค์ ํ ์คํธ ์ฝ๋์์ ์ด๋ ๊ฒ ํธ์ถํ์ฌ ์ํ๋ฅผ ์ค์ ํ๋ค.
mockUseUserStore({ user: { id: 10 } });
