Files
2nd/10_Wiki/Topic_Programming/Architecture/Characterization Tests (특성화 테스트).md
T
Antigravity Agent 9148c358d0 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 폴더 제거.
2026-07-05 00:33:48 +09:00

7.5 KiB

id, title, category, status, canonical_id, aliases, duplicate_of, source_trust_level, confidence_score, verification_status, tags, raw_sources, last_reinforced, github_commit, tech_stack
id title category status canonical_id aliases duplicate_of source_trust_level confidence_score verification_status tags raw_sources last_reinforced github_commit tech_stack
wiki-2026-0508-characterization-tests-특성화-테스트 Characterization Tests (특성화 테스트) 10_Wiki/Topics verified self
Golden Master Tests
Approval Tests
Pinning Tests
Snapshot Tests
none A 0.9 applied
testing
legacy-code
feathers
refactoring
2026-05-10 pending
language framework
typescript-python jest-pytest-approvaltests

Characterization Tests (특성화 테스트)

매 한 줄

"매 legacy code 의 actual behavior 의 capture — 매 spec 이 X, 매 photo 가 O.". Michael Feathers 의 Working Effectively with Legacy Code (2004) 에서 정립. 매 untested legacy 의 refactoring 시작점. 매 "what should it do" X — 매 "what does it do right now" 의 lock down. 2026년 snapshot tests, ApprovalTests, golden master tests 모두 같은 family.

매 핵심

매 정의 (Feathers)

"A characterization test is a test that characterizes the actual behavior of a piece of code. There's no 'correct'... it just records what the system does."

매 vs unit/spec test

측면 Spec Test Characterization Test
Source of truth Requirements / spec Actual current behavior
Failing means Code has bug Behavior changed (maybe intended)
When to write TDD, before code Before refactoring legacy code
Update on change Fix code Review diff, accept if intended

매 procedure (Feathers)

  1. Pick code 의 region.
  2. Write test 가 invokes the code with realistic inputs.
  3. Assert with placeholder (e.g. assert result == "FILL_ME").
  4. Run, capture actual output.
  5. Replace placeholder with actual output.
  6. Now test pins behavior — refactor with confidence.

매 variants

  • Snapshot test (Jest, Vitest): serialize output, compare next run.
  • Approval test (ApprovalTests): write to .approved.txt, manual review on diff.
  • Golden master: large input/output pair, often UI screenshot.
  • Property-based regression: random inputs, save outputs as golden.

매 응용

  1. Refactoring legacy monolith without specs.
  2. Migration (framework upgrade, language port).
  3. Compiler / parser output stability.
  4. Report generation (PDFs, CSVs).
  5. UI visual regression (Percy, Chromatic).

💻 패턴

Feathers procedure (TypeScript + Jest)

import { calculateInvoice } from './legacy';

describe('calculateInvoice — characterization', () => {
  it('records current behavior for typical input', () => {
    const result = calculateInvoice({
      items: [
        { sku: 'A', qty: 2, price: 19.99 },
        { sku: 'B', qty: 1, price: 5.00 },
      ],
      customerType: 'premium',
      country: 'KR',
    });

    // Step 1: write `expect(result).toBe('PLACEHOLDER')`
    // Step 2: run, observe actual:
    //   { subtotal: 44.98, discount: 4.50, vat: 4.05, total: 44.53 }
    // Step 3: paste it as expected
    expect(result).toEqual({
      subtotal: 44.98,
      discount: 4.50,
      vat: 4.05,
      total: 44.53,
    });
  });
});

Jest snapshot

import { renderInvoiceHtml } from './render';

test('invoice html — characterized', () => {
  const html = renderInvoiceHtml(SAMPLE_INVOICE);
  expect(html).toMatchSnapshot();
});
// First run: writes __snapshots__/invoice.test.ts.snap
// Future runs: diffs. Use `--update-snapshot` after intentional change.

ApprovalTests (Python)

from approvaltests import verify

def test_pdf_layout():
    pdf_text = render_pdf(sample_data)
    verify(pdf_text)
# Writes `test_pdf_layout.received.txt`, compares to `.approved.txt`.
# CI fails on diff; dev reviews then renames received→approved.

Golden master with multiple inputs

import json
from pathlib import Path
from legacy_pricing import compute_price

def test_pricing_golden_master():
    cases = json.loads(Path("fixtures/cases.json").read_text())
    actual = [compute_price(c["input"]) for c in cases]
    expected = json.loads(Path("fixtures/expected.json").read_text())
    assert actual == expected

Generating the golden initially

# tools/regenerate_golden.py — run once, then commit
import json
from pathlib import Path
from legacy_pricing import compute_price

cases = json.loads(Path("fixtures/cases.json").read_text())
out = [compute_price(c["input"]) for c in cases]
Path("fixtures/expected.json").write_text(json.dumps(out, indent=2))

Differential testing (old vs refactored)

import { computePriceLegacy } from './pricing-legacy';
import { computePriceNew } from './pricing-new';
import fc from 'fast-check';

test('refactor preserves behavior', () => {
  fc.assert(
    fc.property(
      fc.record({
        qty: fc.integer({ min: 1, max: 100 }),
        unitPrice: fc.float({ min: 0.01, max: 9999 }),
      }),
      (input) => {
        expect(computePriceNew(input))
          .toBeCloseTo(computePriceLegacy(input), 2);
      },
    ),
    { numRuns: 1000 },
  );
});

Capture I/O of legacy via instrumentation

# Wrap legacy fn, log all inputs+outputs in production for a week,
# then replay as test fixtures
from functools import wraps
import json, time

def record(path):
    def deco(fn):
        @wraps(fn)
        def wrapper(*args, **kwargs):
            result = fn(*args, **kwargs)
            with open(path, "a") as f:
                f.write(json.dumps({
                    "ts": time.time(),
                    "args": args, "kwargs": kwargs,
                    "result": result,
                }, default=str) + "\n")
            return result
        return wrapper
    return deco

@record("fixtures/legacy_calls.jsonl")
def legacy_compute(...): ...

매 결정 기준

상황 Approach
Pure function legacy Inline assertion (Feathers procedure)
Large structured output Snapshot or ApprovalTests
Visual UI Storybook + Chromatic / Percy
Two impls (refactor) Differential testing + property-based
Production behavior unknown Record-replay from instrumentation

기본값: Snapshot test for serializable output, ApprovalTests for human-reviewed diffs (PDF, HTML), differential test during refactor.

🔗 Graph

🤖 LLM 활용

언제: legacy code refactor 시작, untested codebase 첫 test net, framework migration, output-shape stability (PDF/CSV/JSON). 언제 X: greenfield TDD (use spec tests), rapidly evolving design (snapshots churn), security-critical (need real spec).

안티패턴

  • Treating snapshot as spec: 매 snapshot fail — auto-update without review. 매 bug 의 silent merge.
  • Huge unreadable snapshot: 1000-line JSON — split into focused snapshots.
  • No fixture review process: golden master changes auto-merge — require reviewer.
  • Characterization without refactor goal: tests forever pin "legacy bug" 의 behavior — note bugs explicitly, fix later.
  • Time/random in capture: nondeterministic snapshots — freeze clock/seed.

🧪 검증 / 중복

  • Verified (Feathers "Working Effectively with Legacy Code" 2004 / Jest docs / ApprovalTests / Beck "Test Driven Development").
  • 신뢰도 A.

🕓 Changelog

날짜 변경
2026-05-08 Phase 1
2026-05-10 Manual cleanup — Feathers procedure + snapshot/approval/differential 패턴