--- id: wiki-2026-0508-mock-objects-가짜-객체 title: Mock Objects (가짜 객체) category: 10_Wiki/Topics status: verified canonical_id: self aliases: [Mock, Mocking, Stubbing, Test Doubles, 테스트 대역] duplicate_of: none source_trust_level: A confidence_score: 0.9 verification_status: applied tags: [testing, tdd, unit-test, mock] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: multi framework: jest-pytest-mockito --- # Mock Objects (가짜 객체) ## 매 한 줄 > **"매 real collaborator 의 자리에 매 control 가능한 가짜를 끼워넣음."**. Mock object 는 매 unit test 에서 매 dependency 를 isolate 하기 위한 매 test double. Gerard Meszaros 의 매 분류 (Dummy / Stub / Spy / Mock / Fake) 가 표준 — 매 잘못 쓰면 매 test 가 매 implementation 에 coupling 된다. ## 매 핵심 ### 매 Test Double 5종 (Meszaros) - **Dummy**: 매 인자 자리만 채움, 매 호출 안됨. - **Stub**: 매 hardcoded response 반환 (state verification). - **Spy**: 매 호출 record (verify 가능한 stub). - **Mock**: 매 expected 호출 정의, 매 위반 시 fail (behavior verification). - **Fake**: 매 실제 working 구현 (in-memory DB 등). ### 매 언제 mock - 매 외부 service (network, DB, FS). - 매 nondeterministic (time, random, env). - 매 expensive (slow, paid API). - 매 hard to trigger (error case, race condition). ### 매 언제 mock 하지 말 것 - 매 own code 의 internal collaborator — 매 over-mocking → 매 fragile test. - 매 third-party library 의 직접 mock — 매 own wrapper 만들고 매 그걸 mock. - 매 value object — 매 그냥 real 사용. ### 매 London vs Detroit (TDD school) - **London (mockist)**: 매 collaborator 다 mock, 매 interaction verify. - **Detroit (classical)**: 매 real object 사용, 매 state verify, 매 boundary 만 mock. - 매 2026 trend: 매 Detroit 우세 (mockist 의 fragile test 비판) — 매 boundary 에서만 mock. ## 💻 패턴 ### Jest (JS) — module mock ```javascript // userService.test.js import { fetchUser } from './userService'; import * as api from './api'; jest.mock('./api'); test('fetchUser returns user data', async () => { api.get.mockResolvedValue({ id: 1, name: 'Alice' }); const user = await fetchUser(1); expect(user.name).toBe('Alice'); expect(api.get).toHaveBeenCalledWith('/users/1'); expect(api.get).toHaveBeenCalledTimes(1); }); ``` ### Jest — spy on real ```javascript import { logger } from './logger'; import { processOrder } from './orders'; test('logs order processing', async () => { const spy = jest.spyOn(logger, 'info'); await processOrder({ id: 'o1' }); expect(spy).toHaveBeenCalledWith('processing', { id: 'o1' }); spy.mockRestore(); }); ``` ### pytest — monkeypatch ```python def test_fetch_user(monkeypatch): def fake_get(url): return {"id": 1, "name": "Alice"} import api monkeypatch.setattr(api, "get", fake_get) from user_service import fetch_user user = fetch_user(1) assert user["name"] == "Alice" ``` ### pytest — unittest.mock ```python from unittest.mock import patch, MagicMock @patch('user_service.api.get') def test_fetch_user(mock_get): mock_get.return_value = {"id": 1, "name": "Alice"} from user_service import fetch_user user = fetch_user(1) mock_get.assert_called_once_with('/users/1') assert user["name"] == "Alice" ``` ### Mockito (Java) ```java import static org.mockito.Mockito.*; @Test void fetchUser_returnsUser() { ApiClient api = mock(ApiClient.class); when(api.get("/users/1")).thenReturn(new User(1, "Alice")); UserService svc = new UserService(api); User u = svc.fetchUser(1); assertEquals("Alice", u.getName()); verify(api).get("/users/1"); verify(api, times(1)).get(anyString()); } ``` ### Fake (in-memory DB) ```typescript // Fake user repo — real working impl, no DB class InMemoryUserRepo implements UserRepo { private users = new Map(); async save(u: User) { this.users.set(u.id, u); } async find(id: string) { return this.users.get(id) ?? null; } } test('UserService.create persists user', async () => { const repo = new InMemoryUserRepo(); const svc = new UserService(repo); await svc.create({ name: 'Alice' }); const all = await repo.find('1'); expect(all?.name).toBe('Alice'); }); ``` ### MSW (Mock Service Worker) — HTTP-level mock ```typescript import { setupServer } from 'msw/node'; import { http, HttpResponse } from 'msw'; const server = setupServer( http.get('/api/users/:id', ({ params }) => HttpResponse.json({ id: params.id, name: 'Alice' }) ) ); beforeAll(() => server.listen()); afterEach(() => server.resetHandlers()); afterAll(() => server.close()); test('component shows user', async () => { render(); expect(await screen.findByText('Alice')).toBeInTheDocument(); }); ``` ### Time mocking ```javascript // Jest fake timers jest.useFakeTimers(); jest.setSystemTime(new Date('2026-05-10T00:00:00Z')); const result = formatRelative(new Date('2026-05-10T00:00:30Z')); expect(result).toBe('30 seconds ago'); jest.useRealTimers(); ``` ## 매 결정 기준 | 상황 | Double type | |---|---| | 외부 HTTP API | MSW (network-level) > module mock | | DB | Fake (in-memory) > Mock | | 시간 / random | jest.useFakeTimers, freeze_time | | 로깅 / metrics | Spy (verify 호출) | | 단순 인자 채우기 | Dummy | | Behavior 검증 (rare) | Mock | | State 검증 (default) | Stub / Fake | **기본값**: 매 boundary 만 mock, 매 internal 은 real. 매 state verify > behavior verify. ## 🔗 Graph - 부모: [[Unit Testing]] · [[TDD]] - 변형: [[Stub]] · [[Spy]] · [[Fake]] · [[Mockito]] · [[MSW]] - 응용: [[Test Pyramid]] · [[Contract Testing]] - Adjacent: [[Dependency Injection]] · [[Hexagonal Architecture]] · [[Mocking_Framework]] ## 🤖 LLM 활용 **언제**: unit test 작성 시 매 외부 dependency isolation, 매 deterministic test, 매 fast feedback. **언제 X**: integration test — 매 real component 가 매 정답. 매 LLM 호출은 매 record/replay (VCR) 가 더 적합. ## ❌ 안티패턴 - **Over-mocking**: 매 internal 까지 mock → 매 refactor 시 매 test 다 깨짐. - **Mock 의 Mock**: 매 mock 이 mock 을 반환 → 매 test 가 매 reality 와 무관. - **Implementation coupling**: 매 `verify(api).callMethod(exact_args)` 의 남발 → 매 fragile. - **Real time / random**: 매 flaky test 의 가장 흔한 원인. - **No teardown**: 매 mock 이 매 다음 test 로 leak — 매 reset 필수. ## 🧪 검증 / 중복 - Verified (Meszaros "xUnit Test Patterns", Martin Fowler "Mocks Aren't Stubs", Jest/pytest/Mockito docs, MSW docs). - 신뢰도 A. - 매 [[Mocking and Stubbing (테스트 대역)]] 는 매 alias redirect. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — Meszaros 5종 + Jest/pytest/Mockito/MSW 패턴 |