Hello Kitty Eyes Shut
๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

๐Ÿ’ป๊ณต๋ถ€ ๊ธฐ๋ก/๐Ÿ“Œ Frontend

[Frontend] Mock์ด๋ž€

๋ฐ˜์‘ํ˜•

 

 

๐Ÿ“ฆ Mock์ด๋ž€

Mock (๋ชจ์˜ ๊ฐ์ฒด)๋ž€ ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์—์„œ

์‹ค์ œ ๊ฐ์ฒด์˜ ๋™์ž‘์„ ํ‰๋‚ด ๋‚ธ ๊ฐ€์งœ ๊ฐ์ฒด๋ฅผ ์˜๋ฏธํ•œ๋‹ค.

 

์ด๋Š” ํ…Œ์ŠคํŠธ ๋Œ€์ƒ์ด ์•„๋‹Œ ์™ธ๋ถ€ ์˜์กด์„ฑ์„ ์ œ์–ดํ•˜๊ฑฐ๋‚˜ ๋Œ€์ฒดํ•จ์œผ๋กœ์จ

ํ…Œ์ŠคํŠธ๋ฅผ ๋” ์‰ฝ๊ณ  ์•ˆ์ •์ ์œผ๋กœ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ค€๋‹ค.

 

์˜ˆ๋ฅผ ๋“ค๋ฉด, ์•„๋ž˜์™€ ๊ฐ™์€ ์ƒํ™ฉ์—์„œ Mock์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 

1๏ธโƒฃ API ์š”์ฒญ์„ ์‹ค์ œ๋กœ ๋ณด๋‚ด์ง€ ์•Š๊ณ , ๋ฏธ๋ฆฌ ์ •ํ•ด๋‘” ์‘๋‹ต์„ ํ‰๋‚ด๋‚ด๊ณ  ์‹ถ์„ ๋•Œ

// userService.ts
export const fetchUser = async () => {
  const res = await fetch('/api/user');
  return res.json();
};

 

fetchUser() ๋ผ๋Š” ํ•จ์ˆ˜๋Š” ์„œ๋ฒ„์— ์š”์ฒญํ•ด์„œ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๋ฐ›์•„์˜ค๋Š” ํ•จ์ˆ˜์ธ๋ฐ,

ํ…Œ์ŠคํŠธ๋ฅผ ํ•  ๋•Œ๋งˆ๋‹ค ์‹ค์ œ ์„œ๋ฒ„์— ์š”์ฒญํ•จ๋…€ ๋А๋ฆฌ๊ณ , ์„œ๋ฒ„๊ฐ€ ์—†์œผ๋ฉด ์—๋Ÿฌ๊ฐ€ ๋‚  ๊ฒƒ์ด๋‹ค.

๐Ÿ‘‰๐Ÿป ๋”ฐ๋ผ์„œ fetch๋ฅผ ๊ฐ€์งœ(mock)๋กœ ๋ฐ”๊ฟ”์น˜๊ธฐ ํ•ด์„œ ๋ฏธ๋ฆฌ ์ •ํ•ด๋‘” ์‘๋‹ต์„ ๋ณด๋‚ด๋„๋ก ํ•ด์ค˜์•ผ ํ•œ๋‹ค.

// userService.test.ts
import { fetchUser } from './userService';

global.fetch = vi.fn(() =>
  Promise.resolve({
    json: () => Promise.resolve({ name: 'ssosso', age: 24 }),
  })
) as jest.Mock;

test('fetchUser๊ฐ€ ์‚ฌ์šฉ์ž ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•œ๋‹ค', async () => {
  const user = await fetchUser();
  expect(user.name).toBe('ssosso');
});

 

global.fetch = ... ๐Ÿ‘‰๐Ÿป ์ „์—ญ์— ์žˆ๋Š” fetch ํ•จ์ˆ˜๋ฅผ ๋ฎ์–ด์“ฐ๊ธฐ ํ•ด์ค€๋‹ค.

 

์ด๋ ‡๊ฒŒ ํ•ด์ฃผ๊ณ  fetchUser()๋ฅผ ์‹คํ–‰ํ•˜๋ฉด,

์šฐ๋ฆฌ๊ฐ€ ์œ„์—์„œ mock์œผ๋กœ ๋งŒ๋“  ๊ฐ€์งœ ์‘๋‹ต์ด ๋Œ์•„์˜ค๋Š”๋ฐ,

๊ทธ ๊ฐ’์ด name: ssosso์ธ์ง€ ํ™•์ธํ•ด์ฃผ๋ฉด ํ…Œ์ŠคํŠธ๊ฐ€ ์™„๋ฃŒ๋œ๋‹ค.


๐Ÿ“Œ ์ฐธ๊ณ : ์™œ global.fetch๋ฅผ ํ•ด์ค˜์•ผ ํ• ๊นŒ?

fetchUser() ๊ฐ™์€ ํ•จ์ˆ˜๋Š” ๋‚ด๋ถ€์—์„œ fetch()๋ฅผ ํ˜ธ์ถœํ•˜๋Š”๋ฐ,

ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์—์„œ ์‹ค์ œ fetch๋ฅผ ์จ๋ฒ„๋ฆฌ๋ฉด,

์ง„์งœ๋กœ ์„œ๋ฒ„์— ์š”์ฒญ์„ ๋ณด๋‚ด๋ ค๊ณ  ํ•œ๋‹ค.

 

๊ทธ๋Ÿฐ๋ฐ ์„œ๋ฒ„๊ฐ€ ์—†๊ฑฐ๋‚˜ ์‘๋‹ต์ด ๋А๋ฆฌ๋ฉด,

ํ…Œ์ŠคํŠธ๋Š” ์‹คํŒจํ•˜๊ฑฐ๋‚˜ ๋А๋ ค์ง€๊ณ , ๋ถˆ์•ˆ์ •ํ•ด์ง„๋‹ค.

 

๋”ฐ๋ผ์„œ ์šฐ๋ฆฌ๋Š” global.fetch = vi.fn(() => Promise.resolve(...)); ๋ฅผ ํ•ด์คŒ์œผ๋กœ์จ

ํ…Œ์ŠคํŠธ ์ค‘์—๋Š” ์‹ค์ œ fetch() ๊ฐ€ ์‹คํ–‰๋˜์ง€ ์•Š๊ณ ,

์šฐ๋ฆฌ๊ฐ€ ์ง€์ •ํ•œ ์‘๋‹ต์„ ์ฆ‰์‹œ ๋ฐ˜ํ™˜ํ•˜๊ฒŒ ํ•ด์ฃผ๋Š” ๊ฒƒ์ด๋‹ค.

 

์ด๋•Œ, ๋ธŒ๋ผ์šฐ์ €์—์„œ๋Š” window.fetch๊ฐ€ fetch ํ•จ์ˆ˜์ด์ง€๋งŒ,

Node ํ™˜๊ฒฝ์—์„œ๋Š” global.fetch์ด๊ธฐ ๋•Œ๋ฌธ์—

ํ…Œ์ŠคํŠธ๋Š” Node ํ™˜๊ฒฝ์—์„œ ๋Œ์•„๊ฐ€๋‹ˆ๊นŒ global.fetch๋ฅผ ๋ฎ์–ด์“ฐ๋Š” ๊ฒƒ์ด๋‹ค.

 


2๏ธโƒฃ ํŠน์ • ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ Œ๋”๋งํ•˜์ง€ ์•Š๊ณ , ๋‹จ์ˆœํ•œ ํ˜•ํƒœ๋กœ ๋Œ€์ฒดํ•˜๊ณ  ์‹ถ์„ ๋•Œ

// App.tsx
import LoginPage from '@/pages/login/LoginPage';

export default function App() {
  return <LoginPage />;
}

 

App ์ปดํฌ๋„ŒํŠธ๋Š” ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋ฅผ ๋ณด์—ฌ์ฃผ๋„๋ก ๊ตฌ์„ฑ๋˜์–ด ์žˆ๋Š”๋ฐ,

์‹ค์ œ LoginPage๊ฐ€ ๋งค์šฐ ๋ณต์žกํ•˜๊ฑฐ๋‚˜ ๋‚ด๋ถ€ API ์š”์ฒญ, ๋ผ์šฐํŒ… ๋“ฑ์ด ํฌํ•จ๋˜์–ด ์žˆ๋”๋ผ๋„

์šฐ๋ฆฌ๋Š” ๋‚ด๋ถ€ ๋™์ž‘๊นŒ์ง€ ๋‹ค ํ…Œ์ŠคํŠธํ•  ํ•„์š”๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์—

mock์„ ํ™œ์šฉํ•ด์„œ ํ…Œ์ŠคํŠธ๋ฅผ ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

// App.test.tsx
jest.mock('@/pages/login/LoginPage', () => {
  const Dummy = () => <div>Mocked Login Page</div>;
  return { __esModule: true, default: Dummy };
});

test('App์— LoginPage๊ฐ€ ๋ Œ๋”๋ง๋˜๋Š”์ง€ ํ™•์ธ', () => {
  render(<App />);
  expect(screen.getByText('Mocked Login Page')).toBeInTheDocument();
});

 

jext.mock() ๐Ÿ‘‰๐Ÿป ํ•ด๋‹น ๋ชจ๋“ˆ์„ ๊ฐ€์งœ๋กœ ๋Œ€์ฒดํ•˜๊ฒ ๋‹ค๋Š” ์„ ์–ธ

Dummy ๐Ÿ‘‰๐Ÿป ์‹ค์ œ LoginPage ๋Œ€์‹  ์‚ฌ์šฉํ•  ๊ฐ€์งœ ์ปดํฌ๋„ŒํŠธ

__esModule: true, default: Dummy ๐Ÿ‘‰๐Ÿป ES Module ๋ฐฉ์‹์˜ default export๋ฅผ ํ‰๋‚ด๋‚ธ ๊ฒƒ

 

์ด๋ ‡๊ฒŒ ํ•ด์คŒ์œผ๋กœ์จ ์šฐ๋ฆฌ๊ฐ€ ํ…Œ์ŠคํŠธํ•  ๋•Œ๋Š”

์‹ค์ œ LoginPage๊ฐ€ ๋ Œ๋”๋ง๋˜๋Š” ๋Œ€์‹ ์—

๊ฐ€์งœ๋กœ ๋ฐ”๊พผ Dummy ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ Œ๋”๋ง ๋˜๊ณ ,

์šฐ๋ฆฌ๋Š” ๊ทธ ์•ˆ์˜ ํ…์ŠคํŠธ "Mocked Login Page"๊ฐ€ ๋‚˜ํƒ€๋‚˜๋Š”์ง€๋งŒ ํ™•์ธํ•˜๋ฉด ๋œ๋‹ค.

 


3๏ธโƒฃ ๋ณต์žกํ•œ ๋‚ด๋ถ€ ๋กœ์ง์„ ์ƒ๋žตํ•˜๊ณ , ์˜ˆ์ƒ๋˜๋Š” ๊ฒฐ๊ณผ๋งŒ ์ง€์ •ํ•ด์„œ ํ…Œ์ŠคํŠธํ•˜๊ณ  ์‹ถ์„ ๋•Œ

// calculator.ts
export const complexOperation = (a: number, b: number) => {
  // ๋‚ด๋ถ€์ ์œผ๋กœ ๋งค์šฐ ๋ณต์žกํ•œ ์ˆ˜ํ•™ ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•จ
  return a * b + Math.sqrt(a + b);
};

 

complexOperation์ด ๋‚ด๋ถ€์ ์œผ๋กœ ๋งค์šฐ ๋ณต์žกํ•œ ์ˆ˜ํ•™ ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๋ฉด,

๊ทธ ๊ฒฐ๊ณผ๋Š” ์˜ˆ์ธกํ•˜๊ธฐ๊ฐ€ ์–ด๋ ต๊ธฐ ๋•Œ๋ฌธ์— ํ…Œ์ŠคํŠธ์— ์‚ฌ์šฉํ•˜๊ธฐ ์ ํ•ฉํ•˜์ง€ ์•Š์„ ๊ฒƒ์ด๋‹ค.

 

// component.tsx
import { complexOperation } from './calculator';

export function DisplayResult() {
  const result = complexOperation(2, 3);
  return <div>Result: {result}</div>;
}

 

์ด ์ปดํฌ๋„ŒํŠธ๋Š” ์‹ค์ œ๋กœ ๋งค์šฐ ๋ณต์žกํ•˜๋‹ค๊ณ  ๊ฐ€์ •ํ•œ

complexOperation(2, 3)์˜ ๊ฒฐ๊ณผ๋ฅผ ํ™”๋ฉด์— ๋ณด์—ฌ์ค€๋‹ค๊ณ  ํ•˜์ž.

 

// component.test.tsx
jest.mock('./calculator', () => ({
  complexOperation: jest.fn(() => 42), // ๊ฒฐ๊ณผ๋งŒ ์ง€์ •
}));

test('๊ฒฐ๊ณผ๊ฐ€ ์ง€์ •๋œ ๊ฐ’์œผ๋กœ ํ‘œ์‹œ๋˜๋Š”์ง€ ํ™•์ธ', () => {
  render(<DisplayResult />);
  expect(screen.getByText('Result: 42')).toBeInTheDocument();
});

 

jest.mock('./calculator', ...) ๐Ÿ‘‰๐Ÿป calculator ๋ชจ๋“ˆ ์ „์ฒด๋ฅผ mocking

complexOperation: jest.fn(() => 42) ๐Ÿ‘‰๐Ÿป ์‹ค์ œ ํ•จ์ˆ˜๊ฐ€ ๋ณต์žกํ•˜๊ฒŒ ๊ณ„์‚ฐํ•˜๋Š” ๊ฑธ ๋ง‰๊ณ , ๊ทธ๋ƒฅ 42๋ผ๋Š” ๊ฐ’๋งŒ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ๋ฐ”๊ฟ”์น˜๊ธฐ ํ•ด์คŒ

 

์ด๋ ‡๊ฒŒ ๋ณ€ํ™˜ํ•ด์ฃผ๋ฉด, ๊ทธ๋ƒฅ Result: 42 ๊ฐ€ ํ™”๋ฉด์— ๋‚˜ํƒ€๋‚˜๋Š”์ง€๋งŒ ํ™•์ธํ•˜๋ฉด ๋˜๊ธฐ ๋•Œ๋ฌธ์—

์‹ค์ œ ์—ฐ์‚ฐ์„ ํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ๋น ๋ฅธ ํ…Œ์ŠคํŠธ๊ฐ€ ๊ฐ€๋Šฅํ•ด์ง„๋‹ค.


๐Ÿ’ก Mock์„ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ 

โœ… ํ…Œ์ŠคํŠธ ์†๋„ ํ–ฅ์ƒ ๐Ÿ‘‰๐Ÿป ์‹ค์ œ API ํ˜ธ์ถœ ๋“ฑ ๋А๋ฆฐ ์ž‘์—… ์—†์ด ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅ

โœ… ์•ˆ์ •์„ฑ ํ™•๋ณด ๐Ÿ‘‰๐Ÿป ์™ธ๋ถ€ ์‹œ์Šคํ…œ ์žฅ์• ๋‚˜ ๋ถˆ์•ˆ์ •์— ์˜ํ–ฅ์„ ๋ฐ›์ง€ ์•Š์Œ

โœ… ๋…๋ฆฝ์„ฑ ๋ณด์žฅ ๐Ÿ‘‰๐Ÿป ํ…Œ์ŠคํŠธํ•˜๋ ค๋Š” ๋Œ€์ƒ๋งŒ ์ง‘์ค‘ํ•ด์„œ ๊ฒ€์ฆ ๊ฐ€๋Šฅ

โœ… ์‹คํŒจ ์›์ธ ์ถ”์  ์šฉ์ด ๐Ÿ‘‰๐Ÿป ์ง„์งœ ๊ตฌํ˜„๊ณผ ๊ด€๊ณ„ ์—†์ด ์˜ˆ์ธก ๊ฐ€๋Šฅํ•œ ๊ฒฐ๊ณผ๋กœ ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅ

 


๐Ÿ”ง Mock๊ณผ ๊ด€๋ จ๋œ ์šฉ์–ด๋“ค

์šฉ์–ด ์„ค๋ช…
Stub ํŠน์ • ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ๋˜๋ฉด ์ •ํ•ด์ง„ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ๋งŒ๋“  ๊ฐ€์งœ
Spy ํ•จ์ˆ˜๊ฐ€ ์–ด๋–ป๊ฒŒ ํ˜ธ์ถœ๋˜์—ˆ๋Š”์ง€๋ฅผ ๊ธฐ๋กํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ์ฒด
Mock Function ํ˜ธ์ถœ ํšŸ์ˆ˜, ์ธ์ž, ๋ฐ˜ํ™˜๊ฐ’ ๋“ฑ์„ ์ž์œ ๋กญ๊ฒŒ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ€์งœ ํ•จ์ˆ˜
Fake ์‹ค์ œ ๋™์ž‘์€ ํ•˜์ง€๋งŒ ๊ตฌํ˜„์ด ๋‹จ์ˆœํ™”๋œ ๋Œ€์ฒด ๊ฐ์ฒด
๋ฐ˜์‘ํ˜•