๋์ด๋์ ๋ง๋ ํ ํ๋ฆฟ์ ์ ํํด์ ์์ฑํด์ฃผ์ธ์!
7์ฃผ์ฐจ ๊ณผ์ ์ฒดํฌํฌ์ธํธ
๊ธฐ๋ณธ
HARD
7์ฃผ์ฐจ ๊ณผ์ ์ฒดํฌํฌ์ธํธ
๊ธฐ๋ณธ๊ณผ์
- ์ด 11๊ฐ์ ํ์ผ, 115๊ฐ์ ๋จ์ ํ ์คํธ๋ฅผ ๋ฌด์ฌํ ์์ฑํ๊ณ ํต๊ณผ์ํจ๋ค.
์ง๋ฌธ
Q. handlersUtils์ ๋จ๊ธด ์ง๋ฌธ์ ๋ต๋ณํด์ฃผ์ธ์.
์ผ๋จ ๋ฌธ์ ๋ handler์์ json์ ์์ ํ๋ handler๊ฐ ์ ์ญ์ผ๋ก ์ค์ ๋์ด์๋ค๋ ์ ์ด๋ค. ๋ณ๋ ฌ์ ์ผ๋ก ํ ์คํธ๊ฐ ์คํ๋ ๊ฒฝ์ฐ ๋ชจ๋๊ฐ ๊ฐ์ json์ ์์ ํ๊ฒ๋๋ ๊ฒ์ด ๋ฌธ์ ๋ค.
์ด๋ป๊ฒํ๋ฉด ๋ณ๋ ฌ์ ์ผ๋ก ํ ์คํธํ ๋ ๋ ๋ฆฝ์ ์ผ๋ก ์คํ์ํฌ๊น?
- test๋ง๋ค ๋ ๋ฆฝ์ ์ผ๋ก events๋ฅผ ์ ํ ํด์ผํ๋ค.
- test๋ง๋ค server handler๊ฐ ๋ ๋ฆฝ์ ์ผ๋ก ๋์ํด์ผํ๋ค.
์งฑ๋์ ๊ณ์ ๊ตด๋ ค๋ด๋ API์์ฒด์์ ์์ ํ๋ ๋ด์ฉ์ด๋ค๋ณด๋ events๋ฅผ ๋ ๋ฆฝ์ ์ผ๋ก ์ ํ ํ๋ ค๋ฉด ๊ฒฐ๊ตญ ํ ์คํธ๋ง๋ค ์๋ก handler๋ฅผ ์์ฑํด์ผ๋๋๊ฒ ์๋๊ฐ? ๋ผ๋ ๊ฒฐ๋ก ์ ์ง์๋ค.
์ด๋ป๊ฒ ํ ์คํธ๋ง๋ค ์๋ก ํธ๋ค๋ฌ๋ฅผ ์ ํ ํ ์ ์์๊น?
1. ํ ์คํธ๋ง๋ค setupServer ์ค์ ํ๊ธฐ
https://mswjs.io/docs/api/setup-server
While we commend setting up request interception globally, as a part of your testing setup, you may also use setupServer in individual tests, if you want to. Just make sure to choose one pattern and follow it throughout your testsโmultiple setupServer calls is not a good idea!
๊ณต์๋ฌธ์๋ฅผ ์ฐธ๊ณ ํด๋ดค์ ๋ setupServer๋ ๊ธ๋ก๋ฒ๋ก ํ๋ฒ ์ ์ธํ๊ฑฐ๋, ํ ์คํธ๋ณ๋ก ์ค์ ํด์ผํ๋ค๊ณ ์ ํ์๋ค.
์ค๋ณต์ผ๋ก ์ ์ธํด์๋ ์๋๋ค.
์ง๊ธ ๊ตฌ์กฐ์์ setupServer๋ฅผ ํ ์คํธ๋ง๋ค ๋์์ํค๋ ค๋ฉด, global๋ก mockingํ๊ณ ์๋ ๋ก์ง์ ์ ๊ฑฐํด์ผํ๋ค.
์ด๋ ๊ฒ๋๋ฉด ๋ ๋ฆฝ์ ์ผ๋ก ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ฅผ ๋์์ํฌ ์๋ ์์ง๋ง, ํน๋ณํ ๋ค๋ฅด๊ฒ ๋์ํด์ผ๋ ์ด์ ๊ฐ ์๋ ๊ณณ์์๋ ๋ถํ์ํ๊ฒ ๋งค๋ฒ setupServer ๋ก์ง์ ๋ฃ์ด์ค์ผํ๋ค.
๋ ์ข์ ๋ฐฉ๋ฒ์ด ์์๊น?
2. handler override ํ๊ธฐ
https://mswjs.io/docs/best-practices/network-behavior-overrides/#resetting-request-handlers
override๋ฅผ ํ ์ ์๋ ๋ฐฉ๋ฒ์ด ์์๊น ์ฐพ์๋ณด๋์ค ๋ฌธ์ ์ ๋ชฉ์ด override์ธ ๋ด์ฉ์ ์ฐพ์๋ค.
server.use()์ resetHandlers()๋ฅผ ์ฌ์ฉํ๋ฉด ๋ด๊ฐ ํด๊ฒฐํ๊ณ ์ ํ๋ ๋ด์ฉ์ ํ์ด๋ผ ์ ์์๋ค.
- ์ ์ญ์ ์ผ๋ก setup server handler๋ค์ด ์กด์ฌ
- ํ ์คํธ ๋ด๋ถ์์ ๊ฐ๊ฐ handler๋ฅผ use()ํจ์๋ฅผ ํตํด override๋ฅผ ํด์ค์ ๋ค๋ฅด๊ฒ ๋์์ํจ๋ค.
- overrideํ ํ ์คํธ์์๋ ํ ์คํธ๊ฐ ๋๋ ๋ resetHandlers()๋ฅผ ์คํ์์ผ ์์๋ณต๊ตฌ
๊ทธ๋ฆผ์ผ๋ก ๊ทธ๋ ค๋ณด๋ฉด ์ด๋ฐ ๋ชจ์ต์ด์ง ์์๊น?
Q. ํ ์คํธ๋ฅผ ๋ ๋ฆฝ์ ์ผ๋ก ๊ตฌ๋์ํค๊ธฐ ์ํด ์์ฑํ๋ ์ค์ ๋ค์ ์๊ฐํด์ฃผ์ธ์. ์์์ ์์ฑํ ๋ด์ฉ์ฒ๋ผ server.use() resetHandlers()๋ฅผ ์ฌ์ฉํ์๋ค.
- ex)
export const setupMockHandlerCreationError = () => {
afterEach(() => {
server.resetHandlers();
});
server.use(
http.post('/api/events', () => {
return HttpResponse.json({ message: 'Error' }, { status: 500 });
})
);
};
์ฌํ ๊ณผ์
- App ์ปดํฌ๋ํธ ์ ์ ํ ๋จ์์ ์ปดํฌ๋ํธ, ํ , ์ ํธ ํจ์๋ก ๋ถ๋ฆฌํ๋๊ฐ?
- ํด๋น ๋ชจ๋๋ค์ ๋ํ ์ ์ ํ ํ ์คํธ๋ฅผ 5๊ฐ ์ด์ ์์ฑํ๋๊ฐ?
ใ ใ ใ ... ์ํ์ต๋๋ค... ์๋.. ๋ชปํ์ต๋๋ค..
๊ณผ์ ์ ํํ๊ณ
ํ ์คํธ์ฝ๋๋ ๋์ ๊ด์ฌ์ฌ๋ ์๋์๊ณ , ์คํํธ์ ์์ ๊ณผ์ฐ ํ์ํ ๋ด์ฉ์ผ๊น? ์ถ์ ์๊ตฌ์ฌ๋ ๊ฐ๊ณ ์์๋ค. ๊ทธ๋ฌ๋ค๋ณด๋ ๋น์ฐํ ํ ์คํธ์ฝ๋ ์์ฒด๋ฅผ ์ฒ์ ๊ณต๋ถํด๋ดค๋ค.
๊ณต๋ถํด๋ณธ ํ๊ธฐ๋ก๋ ์ด๋ ์.. ์ฐ๋ฆฌ ํ์ฌ์์๋ ๋น์ฅ ํ๋์ฉ ์ฌ์ฉํด๋ด๋ ๊ด์ฐฎ๊ฒ ๋๋ฐ...? ๋ผ๋ ์๊ฐ์ ํ๋ค. ํ์ฌ์๋ ํต์ฌ๊ธฐ๋ฅ์ด ์ทํธ์ง, ์ท๋ฑ๋ก, ์ฝ๋๋ฑ๋ก ๋ฑ๋ฑ ์ฑ ์ด๊ธฐ๋ถํฐ ์์๋ ๊ธฐ์ด๊ธฐ๋ฅ์ด์๋ค. ํด๋น ๊ธฐ๋ฅ๋ค์ ๊ฐ์ ํ๊ฑฐ๋ ๊ณ ๋ํํ ๋๋ง๋ค ๋ ๊ฑฐ์ ์ฝ๋๋ค์ด ์ด๋ป๊ฒ ๋์ํ๊ณ ์๋๊ฒ์ด๊ณ , ๊ธฐํ๋ ์ ๋ชจ๋ฅด๊ฒ ๊ณ , ๊ธฐ๋๊ฐ๋ ๋ญ์ง ๋ชจ๋ฅด๊ฒ ๋ ๊ฒฝ์ฐ๊ฐ ๋ง๋ค. ์ด๋ฐ ๊ณณ์ ํ ์คํธ์ฝ๋๋ฅผ ๋์ ํด๋์ผ๋ฉด, ์์ ์ ์ผ๋ก ๊ฐ๋ฐํ ์ ์์ ๊ฒ ๊ฐ๊ณ ํ์๋ค์๊ฒ ๋์ ์ ๊ณ ๋ คํด๋ณด์๊ณ ์ ๋ฌํด๋ ์ํ์ด๋ค.
๊ณผ์ ๋ ๋์ด๋๋ฅผ ์ ํํ ์ ์์๋๋ฐ, ๋๋ hard๋ฅผ ์ ํํ์๋ค. ํ ์คํธ์ฝ๋๋ ์์ ์ฒ์ ์ ํด๋ณด๊ธดํ์ง๋ง, hard ํ ์คํธ ์ฝ๋๋ฅผ ์ ๋ถ ์ํํด์ผ์ง ๊ณต๋ถํ๋ค๊ณ ๋๋ ์ ์์ ๊ฒ ๊ฐ์๊ณ ์ฌํ๊ณผ์ ๋ ์ฒ์๋ถํฐ ํฌ๊ธฐํ๊ณ , ํ ์คํธ์ฝ๋๋ฅผ ํ์ตํ์๋ ๋ชฉํ๋ฅผ ๊ฐ๊ณ ๊ณผ์ ์ ์ํ๋ค. ํ ์คํธ์ฝ๋๋ฅผ ๋ค ์์ฑํ๊ณ ๋ฆฌํฉํ ๋งํ๋ ๊ณผ์ ๋ ๊ฒช์ด๋ณด๊ณ ์ถ์์ง๋ง, ํ ์คํธ์ฝ๋๋ง์ผ๋ก๋ ์ฝ์ง ์์ ์ฃผ๊ฐ์ด์๋ค.
๊ทธ๋๋ ์ค๋๋ง์ AI๋ฅผ ์ฌ์ฉํ์ง ์๊ณ ๊ตฌ๊ธ๋งํ๊ณ ๋ฌธ์๋ฅผ ์ฐพ์๊ฐ๋ฉฐ ์์ฑํ๋ ๊ฒ์ ์ฌ๋ฏธ๋ ๋๊ผ๋ค. ์ค๋๋ง์ ๋ฐ๋ฐ๋ฅ๋ถํฐ ๋ชจ๋ฅด๋ ๊ฒ์ ํ์ตํ๋ ์ฌ๋ฏธ์์๊ณ , AI๋ฅผ ์ฌ์ฉํ์ง ๋ง๊ณ ๊ณผ์ ํ๋ผ๊ณ ํ์ ์ฝ์น๋์ ๊น์ ๋ป์ ๋๋ ์ ์์๋ ์ฃผ๊ฐ์ด์๋ค.
๊ณผ์ ํ๋ฝ์ ๋ณด๋ ๊ฒ์ ๋๋ฌด ์ฌํ์ง๋ง, hard๊ณ ๋ฅธ ๊ณณ์ ํํํ์ง ์๋๋ค! ํ ์คํธ์ฝ๋๋ฅผ ํ์ตํ์ผ๋๊น!!!
๊ธฐ์ ์ ์ฑ์ฅ
ํ ์คํธ ์ฝ๋์ ์ดํด
Testing Library??? Jest?? Vitest??
์ด๋ฒ์ ๋ฐ์ ๋ฅผ ๋ค์ผ๋ฉด์ ํ ์คํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ๋ญ๊ณ , jest์ vitest๋ ๋ฌด์์ด์ง ์ ๋ฆฌ๊ฐ ๋์ง ์์๋ค.
-
Vitest, Jest vitest์ jest๋ ํ ์คํธ ์ฝ๋๋ฅผ ์คํํ๋ ๋ฌ๋์ด๋ค. ํ ์คํธ์ฝ๋๋ฅผ ์คํ์ํค๊ณ ๊ฒฐ๊ณผ๋ฌผ์ ์ถ๋ ฅํด์ฃผ๋ ์์คํ ์ด๋ค.
-
Testing Library? vitest์ jest๋ฅผ ์ด์ฉํ์ฌ ํ ์คํธ๋ฅผ ๋๋ฆด ์ ์๊ฒ ํด์ฃผ๋ ๋๊ตฌ์ด๋ค.
์ฝ๊ฐ ์ง๊ฒฉ์๊ฑฐ์ธ ์ธ๊ณ๊ด์ผ๋ก ๋น๊ตํด๋ณด์๋ฉด,, testing library๋ ์ ์ฒด๊ธฐ๋์ฅ์น test๋ ๊ฑฐ์ธ vitest, jest๋ ์กฐ์ฌ๋ณ๋จ, ์ฃผ๋๋ณ๋จ ์ด๋ฐ ๋๋์ด๋๊น..?
ํ ์คํธ ์์ฑ ๋ฐฉ๋ฒ
ํ ์คํธ ์ฝ๋๋ฅผ ์์ฑํ๋ ๋ฐฉ์์๋ ๋๊ฐ์ง ๋ฐฉ์์ด ์๋ค.
-
๊ตฌํ ๊ธฐ๋ฐ ํ ์คํธ ๊ตฌํ ๊ธฐ๋ฐ ํ ์คํธ๋ ๊ตฌํ ์ธ๋ถ์ฌํญ์ ๋ฐ๋ผ์ ๊ฐ๋ฐ์ ๊ด์ , ์ฝ๋ ๊ตฌ์กฐ ๊ธฐ๋ฐ์ผ๋ก ์์ฑํ๋ ๋ฐฉ์์ด๋ค. ๋ด๋ถ ๋ก์ง์ ๋ํด์ ์ปค๋ฒ๋ฆฌ์ง๊ฐ ๋๋ค.
-
๋ช ์ธ ๊ธฐ๋ฐ ํ ์คํธ ๋ช ์ธ ๊ธฐ๋ฐ ํ ์คํธ๋ ์ฌ์ฉ์ ๊ด์ , ์๊ตฌ์ฌํญ ๋ฌธ์์ ๋ฐ๋ผ์ ์์ฑํ๋ ๋ฐฉ์์ด๋ค.
์๋ฅผ ๋ค์ด์ "์ด๊ธฐ ์ํ์์๋ ์๋ฆผ์ด ์์ด์ผ ํ๋ค" ๋ผ๋ ํ ์คํธ๋ฅผ ์งํํ ๋ ์๋์ด ์์ด์ผ ํ๋ค๋ ๋ช ์ธ ๊ธฐ๋ฐ ํ ์คํธ์ผ๊น ๊ตฌํ ๊ธฐ๋ฐ ํ ์คํธ์ผ๊น?
ํ ์คํธ ์ฝ๋๋ฅผ ์์ฑํด๋ณด์๋ค.
it('์ด๊ธฐ ์ํ์์๋ ์๋ฆผ์ด ์์ด์ผ ํ๋ค', async () => {
const { result: eventsResult } = renderHook(() => useEventOperations(false));
await screen.findByText('์ผ์ ๋ก๋ฉ ์๋ฃ!');
const events = eventsResult.current.events;
const { result } = renderHook(() => useNotifications(events));
expect(result.current.notifications).toEqual([]);
});
useEventOperations, useNotifications ๋ฑ์ hook ๋ด๋ถ์์ ์ด๋ป๊ฒ ๊ตฌํ์ด ๋์ด์๋์ง๋ ํ ์คํธํ์ง ์์๋ค. ์ด๋ฐ ๊ฒฝ์ฐ๊ฐ ๋ช ์ธ ๊ธฐ๋ฐ ํ ์คํธ์ด๋ค.
์ฌ๋ฐ๋ฅธ ํ ์คํธ ์ฝ๋
๋ต๋ณ์ ์ ์ ์ด์ด์ผํ ๊น? ๋์ ์ด์ด๋ ๋ ๊น?
์ฒ์ ๊ณผ์ ๋ฅผ ์งํํ์ ๋๋ ๊ณ ์ ๋ ํ ์คํธ ๋ฐฉ์์ ๋ํด์ ์๋ฌธ์ ํ์๋ค. ๋ฐ์ดํฐ๋ ๋ ์ง๋ฅผ ๋ณ๊ฒฝํ๋ฉด ๋งค๋ฒ ๊ฒฐ๊ณผ๊ฐ์ ํ๋์ฝ๋ฉ์ผ๋ก ๋ค์ ์์ฑํ๋ ๊ฒ์ด ๋งค์ฐ ๋นํจ์จ์ ์ผ๋ก ๋ณด์๋ค. ๊ทธ๋์ ์ ๋ถ ๋์ ์ผ๋ก ๋ต๋ณ์ด ๋์ค๋๋ก ์์ฑํ์๋ค.
it('holidays๋ ์ค๋ ๋ ์ง์ ํด๋นํ๋ ๊ณตํด์ผ ์ ๋ณด๋ฅผ ํฌํจํ๊ณ ์์ด์ผ ํ๋ค', async () => {
const { result } = renderHook(() => useCalendarView());
const holidays = HOLIDAY_RECORD_BY_MONTH[format('YYYY-MM')];
expect(result.current.holidays).toEqual(holidays);
});
it(`currentDate๋ ์ค๋ ๋ ์ง์ธ ${format('YYYY-MM-DD')}์ด์ด์ผ ํ๋ค`, () => {
const { result } = renderHook(() => useCalendarView());
assertDate(result.current.currentDate, new Date());
});
์ฌ๊ธฐ๊น์ง๋ ์๋ง ์ทจํฅ์ฐจ์ด ์ ๋๋ก ๋์ด๊ฐ ์ ์์ ๊ฒ ๊ฐ๋ค. ๋ฌธ์ ์์ ๊ฒ ๊ฐ๋ค.
๊ทธ๋ฌ๋ "๊ฒ์์ด์ ๋ง๋ ์ด๋ฒคํธ๋ง ํํฐ๋งํด์ผ ํ๋ค" ๋ผ๋ ํ ์คํธ์ ๊ฒฐ๊ณผ๊ฐ์ ์ง์ filterํด์ ๊ฒฐ๊ณผ๊ฐ์ ์ถ๋ก ํ๋ค๋ฉด..?
expect(result).toEqual(data.filter(...))
์ด๋ฐ ํ ์คํธ ์ฝ๋๋ฅผ ์์ฑํ๊ณ data.filter()๋.. ํ ์คํธ ๋๋ ค์ผํ๋๊ฑฐ์๋์ผ...? ์๊ฐํ๋ค. ๊ทธ๋ฆฌ๊ณ ์์ฐจ ์ถ์๋ค. ๋ด๊ฐ ๋ญ ์๊ฐ์ํ๊ฑฐ์ผ! ํ๋ฉฐ ๋ฌดํ ํ ์คํธ ๊ตด๋ ์ ๋น ์ง๋ ์์์ ํ๊ณ ์ด๊ฑด ์๋ชป๋๋ค๊ณ ๋๊ผ๋ค.
๊ทธ๋์ ๋ฐ๋ก ์ฝ์น๋๋ค๊ป ์ฌ์ญค๋ดค๋ค. ์ทจํฅ์ฐจ์ด๊ฐ ์๊ธดํ์ง๋ง ํ ์คํธ์ฝ๋์ ๋ต๋ณ์ ์ ์ ์ผ๋ก ์ ๋ ๊ฒ์ด ์ข๋ค๊ณ ํ์ จ๋ค.
ํ ์คํธ์ฝ๋๋ ๋๋ฌด ์ฝ๋ฉํ๋ฏ ์์ฑํ๋ ๊ฒ์ ์ข์ง ์๊ตฌ๋๋ผ๊ณ ๋๊ผ๋ค.
๋๋ฑ ๋ถํ ๊ทธ๋ฃน
ํ ์คํธ ์ค๊ณ ๊ธฐ๋ฒ ์ค ํ๋์ด๋ค.
์ ๋ ฅ๊ฐ๋ค์ ์๋ฏธ์ ์ผ๋ก ๋์ผํ ๊ฒฐ๊ณผ๋ฅผ ๋ด๋ ๊ทธ๋ฃน์ผ๋ก ๋๋๋ ๊ธฐ๋ฒ์ด๋ค.
์๋ ์์๋ฅผ ๋ณด๋ฉด "์ผ์ ์ด ํ์๋์ง ์๋๋ค", "์ผ์ ์ด ํ์๋๋ค" ๋ ๊ทธ๋ฃน์ผ๋ก ๋๋๋ค. ์ด ๋๋ ์๋ฏธ๊ฐ ์๋ ๋๋ฑ ๋ถํ ๊ทธ๋ฃน์ด๋ค.
it('์ฃผ๋ณ ๋ทฐ๋ฅผ ์ ํ ํ ํด๋น ์ฃผ์ ์ผ์ ์ด ์์ผ๋ฉด, ์ผ์ ์ด ํ์๋์ง ์๋๋ค.', async () => {
const user = userEvent.setup();
vi.setSystemTime(new Date('2025-09-01'));
...
expect(screen.queryByText('ํ ํ์')).not.toBeInTheDocument();
});
it('์ฃผ๋ณ ๋ทฐ ์ ํ ํ ํด๋น ์ผ์์ ์ผ์ ์ด ์กด์ฌํ๋ค๋ฉด ํด๋น ์ผ์ ์ด ์ ํํ ํ์๋๋ค', async () => {
const user = userEvent.setup();
vi.setSystemTime(new Date('2025-09-12'));
...
expect(within(weekViewContainer).getByText('ํ ํ์')).toBeVisible();
});
๊ทธ๋ฌ๋ ๋ง์ฝ ์๋์ฒ๋ผ ๊ฐ์ ๋ถํ ๊ทธ๋ฃน์ ํ ์คํธ๋ฅผ ์ฌ๋ฌ๋ฒ ํ๋ ๊ฒฝ์ฐ๋ ์๋ชป๋ ํ ์คํธ ์ค๊ณ๋ค
it('์ฃผ๋ณ ๋ทฐ๋ฅผ ์ ํ ํ 9์ 1์ฃผ์ฐจ์ ์ผ์ ์ด ์์ผ๋ฉด, ์ผ์ ์ด ํ์๋์ง ์๋๋ค.', async () => {
const user = userEvent.setup();
...
expect(screen.queryByText('ํ ํ์')).not.toBeInTheDocument();
});
it('์ฃผ๋ณ ๋ทฐ๋ฅผ ์ ํ ํ 9์ 2์ฃผ์ฐจ์ ์ผ์ ์ด ์์ผ๋ฉด, ์ผ์ ์ด ํ์๋์ง ์๋๋ค.', async () => {
const user = userEvent.setup();
...
expect(screen.queryByText('ํ ํ์')).not.toBeInTheDocument();
});
๊ฒฝ๊ณ๊ฐ ํ ์คํธ
ํ ์คํธ์ ๊ฒฝ๊ณ๊ฐ์๋ ๊ฐ๋ฐ์์ ์ค์๊ฐ ๋ง์ด ์ผ์ด๋ ์ ์๋ ์์น์ด๋ค. ์๋์ฒ๋ผ ๊ฒฝ๊ณ๊ฐ์ด ์ฌ๋ฐ๋ฅด๊ฒ ๋ถ๋ฅ๋๋์ง ํ ์คํธํ๋ ๊ฒ์ ์๋ฏธ์๋ค.
it('์ฃผ์ ๋(์ผ์์ผ)์ ๋ํด ์ฌ๋ฐ๋ฅธ ์ฃผ์ ๋ ์ง๋ค์ ๋ฐํํ๋ค', () => {
expect(getWeekDates(new Date('2025-07-07'))).toEqual([
new Date('2025-07-06'),
new Date('2025-07-07'),
new Date('2025-07-08'),
new Date('2025-07-09'),
new Date('2025-07-10'),
new Date('2025-07-11'),
new Date('2025-07-12'),
]);
});
ํ์ต ํจ๊ณผ ๋ถ์
์ค๋ฌด ์ ์ฉ ๊ฐ๋ฅ์ฑ
ํ์ฌ์๋ ํต์ฌ๊ธฐ๋ฅ์ด ์ทํธ์ง, ์ท๋ฑ๋ก, ์ฝ๋๋ฑ๋ก ๋ฑ๋ฑ ์ฑ ์ด๊ธฐ๋ถํฐ ์์๋ ๊ธฐ์ด๊ธฐ๋ฅ์ด ์๋ค. ํด๋น ๊ธฐ๋ฅ๋ค์ ๊ฐ์ ํ๊ฑฐ๋ ๊ณ ๋ํํ ๋๋ง๋ค ๋ ๊ฑฐ์ ์ฝ๋๋ค์ด ์ด๋ป๊ฒ ๋์ํ๊ณ ์๋๊ฒ์ด๊ณ , ๊ธฐํ๋ ์ ๋ชจ๋ฅด๊ฒ ๊ณ , ๊ธฐ๋๊ฐ๋ ๋ญ์ง ๋ชจ๋ฅด๊ฒ ๋ ๊ฒฝ์ฐ๊ฐ ๋ง๋ค. ์คํ๋ฆฐํธ์์ ํด๋น ์์ ์ ํ๊ฒ ๋๋ค๋ ์๊ธฐ๋ง ๋ค์ด๋ ๋จธ๋ฆฌ๊ฐ ์ํ๊ณ , ์๊ฐ์ด ๋น์ ์์ ์ผ๋ก ๋ง์ด ์๋ชจ๋๋ค.
์๋๋ ์ด์ฉ ์ ์์ง.. ์น ๋ค ์์ด๋ฒ๋ ค์ผ๋๋๋ฐ, ๊ธฐํ๋ถํฐ ๊ฐ๋ฐ๊น์ง ์๋ก ํด์ผ๋๋๊ฑฐ ์๋์ผ? ๋ผ๋ ์๊ฐ๋ง ํ๊ณ ์์๋๋ฐ, ์ด๋ฒ์ ํ ์คํธ ์ฝ๋๋ฅผ ์์ฑํ๋ฉด์ ์๊ฐ์ด ์กฐ๊ธ ๋ฐ๋์๋ค.
์คํ๋์ด Q&A ์น์ ์ค ๋๊ตฐ๊ฐ ๋นํจ์จ์ ์ธ ํ ์คํธ์ฝ๋ ๊ฐ๋ค๊ณ ํด๋ ํ ์คํธ์ฝ๋๋ก ์ธํด ์ฌ๋ฆฌ์ ์ธ ์์ ๊ฐ์ ๋ฐ์ ์ ์๋ค๋ฉด ๊ทธ๊ฑธ๋ก๋ ์๋ฏธ์๋ค๊ณ ํ์ จ๋ค. ์ด ๋ง์์ด ์ข ์๋ฟ๊ฒ ๋๋ ๊ฒ ๊ฐ๋ค. ์๋ ๊ธฐ๋ฅ์ ๊ฑด๋๋ฆด ๋๋ง๋ค ๋ฌด์จ ๋ฒ๊ทธ๊ฐ ๋ฐ์ํ ์ง ๋ชจ๋ฅด๊ฒ ์ผ๋ ๋ถ์ํ๊ณ ์์ ํ๊ณ ์ถ์ง ์์๋๋ฐ, ํ ์คํธ์ฝ๋๋ก ๋์๊ฒ ์ฌ๋ฆฌ์ ์ธ ์์ ๊ฐ์ ์ค๋ค๋ฉด ๊ฐ๋ฐํ๊ธฐ๊ฐ ์ ๋ง ํธํ ๊ฒ ๊ฐ๋ค.
ํ ์คํธ์ฝ๋๋ฅผ ํ์ตํ ํ ์๊ฐ
ํ ์คํธ ์ฝ๋ํ๋ฉด ์์ฒญ ๋ฉ๊ฒ๋ง ๋๊ปด์ก๊ณ , ์ฐ๋ฆฌ ํ์ฌ์ฒ๋ผ ์์, ๋น ๋ฅด๊ฒ ๊ธฐ๋ฅ ๊ฐ๋ฐํ๊ธฐ ๋ฐ์ ๊ณณ์ ์๋ฏธ๊ฐ ์์ ๊ฒ์ด์ผ. ๋ผ๊ณ ๋ง ์๊ฐํ๋๋ฐ, ํ ์คํธ ์ฝ๋๊ฐ ์คํ๋ ค ๋น ๋ฅธ ์ถ์น๋ ฅ์ ์ค ์๋ ์์ ๊ฒ ๊ฐ๋ค๋ ์๊ฐ์ ํ๋ค. ํ์ฌ์์ ํ๋ฌ์ ํ๋ฒ์ฉ ํ ํ๊ณ ๋ฅผ ํ๋๋ฐ, ์ด ๋ ํ ์คํธ ์ฝ๋์ ๋ํ ์๊ฐ์ ๋๋ ๋ณด๋ ค๊ณ ํ๋ค.
๊ณผ์ ํผ๋๋ฐฑ
ํ ์คํธ ์ฝ๋๋ฅผ ์ฒ์ ์ ํ๋ค๋ณด๋ ๊ณผ์ ์์ด ๋ฒ ์ฐจ๊ฒ ๋๊ปด์ง๊ธด ํ์ต๋๋ค. ๊ทธ๋์ ๋์ด๋๋ฅผ ๋๋ ์ฃผ์๊ธด ํ์ จ์ง๋ง, ์ฌ์ด๊ฑธ ๊ณ ๋ฅด๊ณ ์ถ์ง ์์๊ณ ... ์ฌํ๊ณผ์ ๋ฆฌํฉํ ๋ง์ ๊ณผ๊ฐํ๊ฒ ํฌ๊ธฐํ๊ณ ํ ์คํธ์ฝ๋์ ์ง์คํ์ฌ ํ์ตํ๊ณ ์ ํ์์ต๋๋ค. ์ด๋ฐ ๊ฒฝ์ฐ์ ์ ๊ฐ ์ ํํ ๊ธธ์ด๊ธด ํ์ง๋ง ์ฌํ๊ณผ์ ์คํจํด์ ์ฌํ๋ค์ ใ
๋ฆฌ๋ทฐ ๋ฐ๊ณ ์ถ์ ๋ด์ฉ
1. handlersUtils ํจ์ ๋ก์ง
๊ณผ์ ์ ํต์ฌ์ด ๋ณ๋ ฌ๋ก ์คํํ์ ๋ ๋ฌธ์ ๊ฐ ์์ด์ผํ๋ค๊ณ ํ์ จ๋๋ฐ, ํ ์คํธ๊ฐ ๋ณ๋ ฌ๋ก ๋๋ค๋ฉด, ์ฌ๋ฌ๊ณณ์์ ๋์์ server.use๋ก override๋ฅผ ํ๋ฉด ๋ฌธ์ ๊ฐ ์๊ธฐ๋ ๊ฒ์ด ์๋๊ฐ? ํ๋ ๊ฑฑ์ ์ด ๋์์ต๋๋ค.
ํ ์คํธ๋ฅผ ์คํํ์ ๋๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ง ์๋๋ฐ, ํ ์คํธ๊ฐ ๋ณ๋ ฌ๋ก ๋๋ค๋ ์๋ฏธ๊ฐ ๋์์ ํ ์คํธ๊ฐ ์คํ๋๋ค๋ ๊ฒ์ ์๋๊ฑธ๊น์?
server.resetHandlers()๋ฅผ ํด์ฃผ์ง ์์ผ๋ฉด ์ธ๋ถ ํ ์คํธ์ ์ํฅ์ฃผ๋ ๊ฒ์ ๋ณด์์ ๋๋ ๊ทธ๋ ์ง๋ง์ ์๋๊ฑฐ๊ฐ๊ณ .. ์ฝ๊ฐ ํผ๋์ค๋ฌ์ด ๋ถ๋ถ์ ๋๋ค.
2. ํ์ฌ์์ ํ ์คํธ ์ฝ๋ ๋์ ํ๋ ๋ฐฉ๋ฒ?
์์ ํ๊ณ ์ ์ ์ ๊ฒ์ฒ๋ผ ๋ ๊ฑฐ์ ๋ก์ง๊ณผ ํจ๊ป ๊ฐ๋ฐํ ๋ ํ์์ฑ์ด ๋๊ปด์ง ๊ฒ ๊ฐ์ต๋๋ค. ํด๋น ํฌ์ธํธ๋ก ํ์๋ค๊ณผ ์๊ธฐํด์ ๋์ ์ ํด๋ณผ๊น ์ถ์๋ฐ, ์ค์ผ๋์ ์ด๋ป๊ฒ ์๊ฐํ์๋์? ์ ๊ฐ ์๋ก์ด๊ฑฐ ๋ฐฐ์ ๋ค๊ณ ์ ๋์ ํธํ๋ ์๊ฐ์ผ๋ก ์ง๊ธ ์ํฉ์ ๋ฐ๋ผ๋ณธ๊ฒ ์๋๊น? ๊ฑฑ์ ๋์์ต๋๋ค.
3. ํ ์คํธ ์ฝ๋์ ๊ธฐ๋๊ฐ์ ์ด๋๊น์ง ์ ์ ์ผ๋ก ๋์ด์ผํ๊ณ ๋์ ์ผ๋ก ๋ง๋ค์ด์ผํ ์ง? (์ด๋ฏธ ์คํ์ฝ์น๋๊ป์ ๋ต๋ณ์ ํด์ฃผ์ จ์ง๋ง, ์ค์ผ๋๊ป๋ ๋ค์ด๋ณด๊ณ ์ถ๋ค์ ใ ใ ..)
์๋ฅผ ๋ค์ด์ "๊ฒ์์ด์ ๋ง๋ ์ด๋ฒคํธ๋ง ํํฐ๋งํด์ผ ํ๋ค" ๋ผ๋ ํ ์คํธ์์์ ๊ฒฐ๊ณผ ๊ธฐ๋๊ฐ์ด ์๋๋ผ๋ฉด,
[
{
id: '2',
title: '์ค์ผ๋ ์งฑ์งฑ๋งจ',
date: '2025-10-15',
startTime: '09:00',
endTime: '10:00',
description: '์ค์ผ๋ ์งฑ์งฑ๋งจ',
location: 'ํ์์ค B',
category: '์
๋ฌด',
repeat: { type: 'none', interval: 0 },
notificationTime: 10,
},
];
์ ๋ต์ ๋ํ ๊ธฐ๋๊ฐ์ ์ ์ ์ผ๋ก ๋ณด๊ดํด์ผํ ์ง ์๋์ฒ๋ผ filter๋ก ๋์์ผ๋๋ ๊ณ ๋ฏผํ์์ต๋๋ค.
eventList.filter(...)
filter์ ๋ก์ง์ด ๋๋์๊ฐ ํ ์คํธ์ฝ๋๋ฅผ ํ ์คํธํด์ผ๋๋ ์ํฉ์ด ์ค๋๊ฒ ์๋๊ฐ? ๋ผ๋ ๋ฌดํ ํ ์คํธ ๊ตด๋ ์ ๋น ์ง๋ ์์์ ํด๋ณด์์ต๋๋ค.
๊ทธ๋ผ ๋ฌด์กฐ๊ฑด ์ ์ ์ผ๋ก ๋์ด์ผํ๋? ์ถ๋ค๊ฐ๋ ์์ฃผ ๊ฐ๋จํ ๊ฒฐ๊ณผ๊ฐ์ธ๋ฐ๋ ์ ์ ์ผ๋ก ๋๋ ๊ฒ์ ๋ ๋ถํ์ํ ๊ฒ ๊ฐ์ต๋๋ค.
์์๋ก ๋ค์ด๋ณด๋ฉด, "์ฃผ๊ฐ ๋ทฐ์์ ๋ค์์ผ๋ก navigate" ํ๋ ํ ์คํธ์ ๋๋ค.
7์ผํ, 7์ผ์ ์ผ๋ก ์ด๋ํ๋ ์์ฃผ ๊ฐ๋จํ ๋ก์ง์์๋ ์ด๊ฒ๋ ์ ์ ์ธ ๋ฐ์ดํฐ๋ก ํ ์คํธ๊ฒฐ๊ณผ๊ฐ์ ์์ฑํด๋์ผํ ์ง ๊ณ ๋ฏผ์ ๋๋ค.
์ ์ ์ผ๋ก ๋๋ฉด mock ๋ฐ์ดํฐ๊ฐ ๋ณ๊ฒฝ๋๋ฉด ํ ์คํธ ๊ฒฐ๊ณผ๊ฐ๋ ๊ฐ์ด ๋งค๋ฒ ์์ ํด์ค์ผํ๋ค๋ ๋ถํธํ ์ ์ด ์์ํ ๋ฐ, ๋ถํธํ๋๋ผ๋ ์ฌ๋ฐ๋ฅธ ํ ์คํธ๋ฅผ ์์ฑํ๋ ค๋ฉด ์ ์ ์ธ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํด์ผํ ๊น์?
๊ณผ์ ํผ๋๋ฐฑ
์๋ ํ์ธ์ ์ง์๋! 7์ฃผ์ฐจ ๊ณผ์ ์ ์งํํด์ฃผ์ จ๋ค์ ใ ใ ๋ค๋ง ์ฌํ๊ณผ์ ๋ ์๊ฐ์ด ๋ง์ด ๋ถ์กฑํ๊ตฐ์.. ใ ใ ์์ฝ์ต๋๋ค.
handlersUtils ํจ์ ๋ก์ง: ๊ณผ์ ์ ํต์ฌ์ด ๋ณ๋ ฌ๋ก ์คํํ์ ๋ ๋ฌธ์ ๊ฐ ์์ด์ผํ๋ค๊ณ ํ์ จ๋๋ฐ, ํ ์คํธ๊ฐ ๋ณ๋ ฌ๋ก ๋๋ค๋ฉด, ์ฌ๋ฌ๊ณณ์์ ๋์์ server.use๋ก override๋ฅผ ํ๋ฉด ๋ฌธ์ ๊ฐ ์๊ธฐ๋ ๊ฒ์ด ์๋๊ฐ? ํ๋ ๊ฑฑ์ ์ด ๋์์ต๋๋ค. ํ ์คํธ๋ฅผ ์คํํ์ ๋๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ง ์๋๋ฐ, ํ ์คํธ๊ฐ ๋ณ๋ ฌ๋ก ๋๋ค๋ ์๋ฏธ๊ฐ ๋์์ ํ ์คํธ๊ฐ ์คํ๋๋ค๋ ๊ฒ์ ์๋๊ฑธ๊น์? server.resetHandlers()๋ฅผ ํด์ฃผ์ง ์์ผ๋ฉด ์ธ๋ถ ํ ์คํธ์ ์ํฅ์ฃผ๋ ๊ฒ์ ๋ณด์์ ๋๋ ๊ทธ๋ ์ง๋ง์ ์๋๊ฑฐ๊ฐ๊ณ .. ์ฝ๊ฐ ํผ๋์ค๋ฌ์ด ๋ถ๋ถ์ ๋๋ค.
ํ ์คํธ ๋ณ๋ ฌ ์คํ์ ์๋ฏธ
Jest/Vitest์์ "๋ณ๋ ฌ ์คํ"์ ์๋ก ๋ค๋ฅธ ํ ์คํธ ํ์ผ๋ค์ ๋ณ๋ ฌ๋ก ์คํํ๋ ๊ฒ์ ์๋ฏธํ๋ต๋๋ค!
# ์ด๋ฐ ์์ผ๋ก ์ฌ๋ฌ ํ์ผ์ด ๋์์ ์คํ๋ฉ๋๋ค
test-file-1.test.ts (Worker 1) โ ๋
๋ฆฝ์ ์ธ ํ๋ก์ธ์ค
test-file-2.test.ts (Worker 2) โ ๋
๋ฆฝ์ ์ธ ํ๋ก์ธ์ค
test-file-3.test.ts (Worker 3) โ ๋
๋ฆฝ์ ์ธ ํ๋ก์ธ์ค
ํ์ง๋ง ๋ณ๋์ ์ค์ ์ ํด์ฃผ์ง ์๋๋ค๋ฉด ํ๋์ ํ ์คํธ ํ์ผ ๋ด๋ถ์์๋ ํ ์คํธ๋ค์ด ์์ฐจ์ ์ผ๋ก ์คํ๋๋ต๋๋ค..!
๊ณต์๋ฌธ์ ๋งํฌ: https://vitest.dev/guide/parallelism.html#file-parallelism
MSW Server ์ธ์คํด์ค
๊ฐ ํ ์คํธ ํ์ผ์ ๋ ๋ฆฝ์ ์ธ MSW server ์ธ์คํด์ค๋ฅผ ๊ฐ์ง๋๋ค.
// setupTests.ts์์ ์์ฑ๋ server๋ ๊ฐ ํ์ผ๋ง๋ค ๋
๋ฆฝ์
export const server = setupServer(...handlers);
๋ฐ๋ผ์ ํ์ผ A์์ server.use()๋ฅผ ํธ์ถํด๋ ํ์ผ B์ server์๋ ์ํฅ์ ์ฃผ์ง ์๋๋ต๋๋ค!
์ฆ, ๋ค๋ฅด๊ฒ ์ด์ผ๊ธฐํด๋ณด์๋ฉด ๊ฐ์ ํ์ผ ๋ด์ ํ
์คํธ ๊ฐ์๋ ๋ฌธ์ ๊ฐ ์๊ธธ์๋ ์์ด์.
// ๊ฐ์ ํ
์คํธ ํ์ผ์์
describe('Event tests', () => {
test('should handle creation error', () => {
setupMockHandlerCreationError(); // server.use()๋ก POST ํธ๋ค๋ฌ ์ถ๊ฐ
// ํ
์คํธ ์คํ...
});
test('should handle normal flow', () => {
// ์ด์ ํ
์คํธ์ POST ํธ๋ค๋ฌ๊ฐ ์ฌ์ ํ ํ์ฑํ๋์ด ์์ ์ ์์!
// afterEach๋ ํ
์คํธ ์๋ฃ ํ์ ์คํ๋๋ฏ๋ก
});
});
๊ทธ๋์ ์ด๋ฌํ ์ ๊ทผ์ด ํ์ํ ์ ์๋ต๋๋ค!
export const createMockHandlerScope = (handlers: any[]) => {
let originalHandlers: any[];
return {
setup: () => {
// ํ์ฌ ํธ๋ค๋ฌ ๋ฐฑ์
originalHandlers = server.listHandlers();
server.use(...handlers);
},
cleanup: () => {
// ์ ํํ ์ด์ ์ํ๋ก ๋ณต์
server.resetHandlers(...originalHandlers);
}
};
};
// ๋๋ ํ
์คํธ๋ณ ๋
๋ฆฝ์ ์ธ ์ค์
export const withMockHandlers = (handlers: any[], testFn: () => void) => {
const originalHandlers = server.listHandlers();
beforeEach(() => {
server.use(...handlers);
});
afterEach(() => {
server.resetHandlers(...originalHandlers);
});
return testFn;
};
ํ์ฌ์์ ํ ์คํธ ์ฝ๋ ๋์ ํ๋ ๋ฐฉ๋ฒ: ์์ ํ๊ณ ์ ์ ์ ๊ฒ์ฒ๋ผ ๋ ๊ฑฐ์ ๋ก์ง๊ณผ ํจ๊ป ๊ฐ๋ฐํ ๋ ํ์์ฑ์ด ๋๊ปด์ง ๊ฒ ๊ฐ์ต๋๋ค. ํด๋น ํฌ์ธํธ๋ก ํ์๋ค๊ณผ ์๊ธฐํด์ ๋์ ์ ํด๋ณผ๊น ์ถ์๋ฐ, ์ค์ผ๋์ ์ด๋ป๊ฒ ์๊ฐํ์๋์? ์ ๊ฐ ์๋ก์ด๊ฑฐ ๋ฐฐ์ ๋ค๊ณ ์ ๋์ ํธํ๋ ์๊ฐ์ผ๋ก ์ง๊ธ ์ํฉ์ ๋ฐ๋ผ๋ณธ๊ฒ ์๋๊น? ๊ฑฑ์ ๋์์ต๋๋ค.
ํ ์คํธ๋ฅผ ์ ๋ง ๋์ ํ๊ณ ์ถ๋ค๋ฉด, ํ์ "ํ ์คํธ"์ ๋ํ ์ด์ผ๊ธฐ๋ฅผ ๊บผ๋ด์ง ์๊ณ ์ผ๋จ ํ ์คํธ๋ฅผ ์์ฑํด์ PR์ ์ฌ๋ฆฐ๋ค์์ "์ด๋ ๊ฒ ํ๋๊น ~~ ๋ฑ์ ํจ๊ณผ๋ฅผ ๋๊ผ์ด์!" ๋ผ๊ณ ์ถ๊ฐํด๋ณด๋๊ฑฐ์ฃ ใ ใ
๋ฌด์กฐ๊ฑด ๋ชจ๋ ์ฝ๋์ ๋์ ํ๊ธฐ๋ณด๋จ ๊ทธ๋ฅ ์ง์๋์ด ํ์ํ๋ค๊ณ ์๊ฐํ๋ ๋ถ๋ถ์ ํ ์คํธ๋ฅผ ์ถ๊ฐํด์ ๋์ ํ๋ฉด ๋๋ค๊ณ ์๊ฐํด์.
์ด๋ ๊ฒ ์ผ๋จ ๋์ ๋ณด์ด๋ ๋ฐฉ์์ผ๋ก ํ ์คํธ๋ฅผ ๋ณด์ฌ์ค ๋ค์์ ํ์์ ๋ฐ์์ ์ดํด๋ณด์ธ์. "๊ตณ์ด?" ๋ผ๋ ํค์๋๊ฐ ๊ณ์ ๋ ธ์ถ๋๋ค๋ฉด.. ์๋ง ๋์ ํ๊ธฐ ํ๋ค ์ ์๋ต๋๋ค ใ ใ
ํ ์คํธ ์ฝ๋์ ๊ธฐ๋๊ฐ์ ์ด๋๊น์ง ์ ์ ์ผ๋ก ๋์ด์ผํ๊ณ ๋์ ์ผ๋ก ๋ง๋ค์ด์ผํ ์ง? (์ด๋ฏธ ์คํ์ฝ์น๋๊ป์ ๋ต๋ณ์ ํด์ฃผ์ จ์ง๋ง, ์ค์ผ๋๊ป๋ ๋ค์ด๋ณด๊ณ ์ถ๋ค์ ใ ใ ..)
์ด๋ฐ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ๋ชฉ๋ฐ์ดํฐ๊ฐ ์๋ค๊ณ ์๊ฐํด์ ใ ใ ๊ธฐ๋๊ฐ์ด ๋ฐ๋์ง ์๋๋ก ๋ชฉ๋ฐ์ดํฐ๋ฅผ ํตํด ๊ณ ์ ํด๋๋๊ฑฐ์ฃ . ๊ทธ๋ฌ๋ฉด ๊ผญ ์ด๋ด ๋ ๋ชฉ๋ฐ์ดํฐ๋ฅผ ์จ์ผํ๋๊ฐ!? ๋ผ๊ธฐ ๋ณด๋จ... ์ต์ํ์ ๊ณ ์ ์ด ํ์ํ๋ค๊ณ ์๊ฐํด์. ๊ฐ๋ น ๋ ์ง๋ง ๊ณ ์ ํด๋๋๋ค๊ฑฐ๋!?
์ฌ์ด๋ ์ดํํธ๊ฐ ์ต์ํ๋ ๋ก์ง์ด๋ผ๋ฉด ์ด๋ค input์ด๋ output์ ๋ฑ์ด๋ด๋ ๋ฐฉ์์ ๋์ผํ ํ ๋๊น์! ๋์ ์ฃ์ง์ผ์ด์ค(๊ฒฝ๊ณ๊ฐ)์ ํ ์คํธ ์ผ์ด์ค์ ๊ผผ๊ผผํ๊ฒ ์ถ๊ฐํด์ฃผ๋ฉด ์ข๋ต๋๋ค!