docs(10_Wiki): 위키 전체 재구성 — Topic_* 폴더를 4개 카테고리로 통합 + 대규모 중복 제거
Topic_Agent/Topic_Blog/Topics/Topics_Biz/Topics_Meeting/Topics_Rag의 마크다운 지식 문서를 Topic_General/Topic_Programming/Topic_Graphic/Topic_Business 4개 카테고리로 재분류. - 중복 제거: frontmatter의 status:duplicate/merged + duplicate_of/redirect_to 필드로 자기 자신을 중복으로 선언한 리다이렉트 stub 1032개 제거, 완전 동일 내용 파일 472개 제거, 동일 파일명·다른 내용 충돌 시 더 큰(완전한) 버전만 유지(162개 제거) — 총 1639개 중복 제거. - 분류: 폴더 단위로 명확한 항목(AI_and_ML/Coding/Architecture 등 → Programming, Comfyui/Visual_Effects → Graphic, Topics_Biz/Topics_Meeting/사업 등 → Business, Poetic_Blog_Writing/창의성/Game_Design 등 → General)은 폴더 우선순위로, 나머지 혼재 폴더(Topic_Agent/Topic_Blog/Topics 루트/Thinking & Reasoning/Other/UI_UX_Assets)는 title/tags 키워드 스코어링으로 파일 단위 분류(불명확한 경우 General로 폴백). 원본 폴더명은 "From_*" 서브폴더로 보존해 추적 가능성 유지. - 최종 배치: Programming 2784 / General 1608 / Graphic 285 / Business 249 = 4926개 문서. - 에이전트 운영 상태(.astra/.agent/.obsidian/sessions/memory/_company/docs/lessons/_shared/src)는 지식 콘텐츠가 아니므로 재분류 대상에서 제외하고 원위치 유지. - Topics/Topic_email(상위 보호 폴더 Topic_email과 파일명 100% 중복) 삭제 — 보호 폴더 자체는 미변경. - 완전히 비게 된 Topic_Agent/Topic_Blog/Topics_Biz/Topics_Rag 폴더 제거.
This commit is contained in:
@@ -0,0 +1,222 @@
|
||||
---
|
||||
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<string, User>();
|
||||
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(<UserCard id="1" />);
|
||||
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 패턴 |
|
||||
Reference in New Issue
Block a user