--- id: wiki-2026-0508-unit-tests-단위-테스트 title: Unit Tests (단위 테스트) category: 10_Wiki/Topics status: verified canonical_id: self aliases: [단위 테스트, Unit Testing, Unit Test] duplicate_of: none source_trust_level: A confidence_score: 0.9 verification_status: applied tags: [testing, unit-test, tdd, vitest] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: typescript framework: vitest-2 --- # Unit Tests (단위 테스트) ## 매 한 줄 > **"매 작은 unit (function / class) 을 isolate 해서 매 fast 하게 검증"**. 매 test pyramid 의 base. 2026 JS 진영은 Vitest 가 매 default — fast, ESM-native, jest-compatible API. ## 매 핵심 ### 매 properties (FIRST) - **Fast**: ms 단위. - **Independent**: 순서 무관. - **Repeatable**: deterministic. - **Self-validating**: pass/fail 명확. - **Timely**: 매 production code 와 동시 (또는 먼저). ### 매 무엇이 unit - 매 단일 function / 매 class 의 method / 매 small module. - 매 collaborator 는 stub/mock 또는 매 real (sociable test). - 매 file system / network / DB 는 통상 test 가 unit 아님. ### 매 응용 1. TDD red-green-refactor. 2. Regression guard. 3. Living documentation. 4. Refactoring safety net. ## 💻 패턴 ### Vitest basic (AAA) ```typescript // sum.ts export function sum(a: number, b: number): number { return a + b; } // sum.test.ts import { describe, expect, it } from 'vitest'; import { sum } from './sum'; describe('sum', () => { it('adds two positive numbers', () => { // Arrange const a = 2, b = 3; // Act const result = sum(a, b); // Assert expect(result).toBe(5); }); it('handles negatives', () => { expect(sum(-1, 1)).toBe(0); }); }); ``` ### Parameterized ```typescript import { describe, expect, it } from 'vitest'; describe.each([ { a: 1, b: 2, want: 3 }, { a: 0, b: 0, want: 0 }, { a: -1, b: 1, want: 0 }, ])('sum($a, $b)', ({ a, b, want }) => { it(`= ${want}`, () => { expect(sum(a, b)).toBe(want); }); }); ``` ### Mock collaborator ```typescript import { vi, expect, it } from 'vitest'; interface Mailer { send(to: string, body: string): Promise; } class SignupService { constructor(private mailer: Mailer) {} async signup(email: string) { await this.mailer.send(email, 'Welcome!'); } } it('sends welcome email on signup', async () => { const mailer: Mailer = { send: vi.fn().mockResolvedValue(undefined) }; const svc = new SignupService(mailer); await svc.signup('a@b.c'); expect(mailer.send).toHaveBeenCalledWith('a@b.c', 'Welcome!'); }); ``` ### Module mock ```typescript import { vi } from 'vitest'; vi.mock('./db', () => ({ findUser: vi.fn().mockResolvedValue({ id: '1', name: 'A' }), })); ``` ### Time / clock ```typescript import { afterEach, beforeEach, vi, expect, it } from 'vitest'; beforeEach(() => vi.useFakeTimers()); afterEach(() => vi.useRealTimers()); it('expires after 1h', () => { const session = createSession({ ttlMs: 3600_000 }); expect(session.expired()).toBe(false); vi.advanceTimersByTime(3600_001); expect(session.expired()).toBe(true); }); ``` ### Property-based (fast-check) ```typescript import fc from 'fast-check'; import { it, expect } from 'vitest'; it('reverse(reverse(xs)) == xs', () => { fc.assert( fc.property(fc.array(fc.integer()), (xs) => { expect([...xs].reverse().reverse()).toEqual(xs); }), ); }); ``` ### Snapshot (use sparingly) ```typescript it('renders config', () => { expect(buildConfig({ env: 'prod' })).toMatchInlineSnapshot(` { "apiUrl": "https://api.example.com", "retries": 3, } `); }); ``` ### React component (Testing Library) ```tsx import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { Counter } from './Counter'; it('increments on click', async () => { const user = userEvent.setup(); render(); await user.click(screen.getByRole('button', { name: /\+/ })); expect(screen.getByText('Count: 1')).toBeInTheDocument(); }); ``` ### Coverage ```bash vitest run --coverage ``` ```typescript // vitest.config.ts export default { test: { coverage: { provider: 'v8', reporter: ['text', 'html'], thresholds: { lines: 80, functions: 80, branches: 70 }, }, }, }; ``` ## 매 결정 기준 | 상황 | Approach | |---|---| | 매 pure logic | Plain unit test | | 매 collaborator-heavy | Sociable test (real collaborator) 또는 mock | | 매 algorithmic invariants | Property-based (fast-check) | | 매 React component | Testing Library — user-centric | | 매 stable structured output | Snapshot (sparingly) | | 매 DB / network | 매 integration test (not unit) | **기본값**: 매 sociable + AAA + named cases. 매 mock 은 매 boundary 에서만. ## 🔗 Graph - 부모: [[Test_Pyramid]] - 응용: [[TDD]] · [[Mocking]] · [[Test_Doubles]] ## 🤖 LLM 활용 **언제**: 매 test scaffolding, 매 edge case 생성, 매 missing branch coverage 의 fill. **언제 X**: 매 fundamental design problem — 매 test 만으로는 fix 안됨. ## ❌ 안티패턴 - **Test implementation**: 매 internal 의 mock — 매 refactor breakage. - **Slow units**: 매 DB / network → unit 아님. - **Shared state**: 매 order-dependent flake. - **Snapshot 남발**: 매 update 만 누름 — 매 검증 안됨. - **Coverage as goal**: 매 100% line ≠ 매 quality. ## 🧪 검증 / 중복 - Verified (Vitest 2.x docs, Beck "Test-Driven Development", Meszaros "xUnit Test Patterns", 2026). - 신뢰도 A. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — Vitest unit tests with mocks, fake timers, fast-check |