vitest-patterns
Clauday 의 vitest 단위 테스트 작성 패턴 (Electron main/preload 의존 코드, 시간 의존, 플랫폼 분기, IPC 핸들러). 테스트 작성 시 트리거.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Clauday 의 vitest 단위 테스트 작성 패턴 (Electron main/preload 의존 코드, 시간 의존, 플랫폼 분기, IPC 핸들러). 테스트 작성 시 트리거.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
Use when: developer 페이즈 진입 시, BE/FE 코드 변경을 실행해야 할 때.
Use when: QA 페이즈에서 수락기준 검증이 필요할 때.
Use when: 코드 리뷰가 필요할 때.
Use when: developer 페이즈에서 코드 변경이 필요할 때.
AIService.runClaudeStream 또는 ClaudeChatService 의 claude CLI spawn 코드를 수정할 때 Windows/Mac 분기 함정을 피하기 위한 안전 체크리스트. ai-service 또는 claude-chat 도메인 작업 시 반드시 트리거.
feature/<도메인>/<task-id>/ 산출물 (prd, adr, plan, impl-log, qa-report) 의 YAML frontmatter / 필수 섹션 / 불변성 검증. integrator 가 게이트 진입 시 호출.
| name | vitest-patterns |
| description | Clauday 의 vitest 단위 테스트 작성 패턴 (Electron main/preload 의존 코드, 시간 의존, 플랫폼 분기, IPC 핸들러). 테스트 작성 시 트리거. |
vitest (watch 모드 없음, --run 으로 1회 실행)vitest.config.tsnpm test — 전체 1회npx vitest run <path> — 특정 파일/디렉터리FooService.ts ↔ FooService.test.tstest/ 디렉터리 (현재 적게 사용)import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
import { FooService } from './FooService'
describe('FooService', () => {
let service: FooService
beforeEach(() => {
service = new FooService()
})
describe('list', () => {
it('빈 상태에서 빈 배열 반환', () => {
expect(service.list()).toEqual({ items: [] })
})
it('add 후 list 시 추가된 항목 포함', () => {
service.add({ id: '1', name: 'a' })
expect(service.list().items).toHaveLength(1)
})
})
})
electron import 가 필요한 곳은 모듈 boundary 에서 mock:
vi.mock('electron', () => ({
app: { getPath: vi.fn().mockReturnValue('/tmp/clauday-test') },
BrowserWindow: vi.fn(),
ipcMain: { handle: vi.fn() },
}))
권장: electron 직접 의존을 service 클래스에서 분리. service 는 인자로 userDataPath 같은 의존 받고, IPC 핸들러 어댑터에서만 app.getPath 등 호출.
vi.mock('electron-store', () => {
const map = new Map()
return {
default: vi.fn().mockImplementation(() => ({
get: (k: string) => map.get(k),
set: (k: string, v: unknown) => { map.set(k, v) },
delete: (k: string) => { map.delete(k) },
})),
}
})
vi.mock('keytar', () => ({
default: {
getPassword: vi.fn().mockResolvedValue(null),
setPassword: vi.fn().mockResolvedValue(undefined),
deletePassword: vi.fn().mockResolvedValue(true),
},
}))
vi.mock('child_process', () => ({
spawn: vi.fn().mockImplementation(() => ({
stdout: { on: vi.fn() },
stderr: { on: vi.fn() },
stdin: { write: vi.fn(), end: vi.fn(), on: vi.fn() },
on: vi.fn((event, cb) => {
if (event === 'close') setTimeout(() => cb(0), 0)
}),
kill: vi.fn(),
})),
execFile: vi.fn(),
execFileSync: vi.fn().mockReturnValue(Buffer.from('mock-output')),
}))
beforeEach(() => { vi.useFakeTimers() })
afterEach(() => { vi.useRealTimers() })
it('3초 후 timeout', () => {
const cb = vi.fn()
setTimeout(cb, 3000)
vi.advanceTimersByTime(3000)
expect(cb).toHaveBeenCalled()
})
const originalPlatform = process.platform
afterEach(() => {
Object.defineProperty(process, 'platform', { value: originalPlatform, configurable: true })
})
it('Mac 분기', () => {
Object.defineProperty(process, 'platform', { value: 'darwin', configurable: true })
// ... 검증
})
it('Windows 분기', () => {
Object.defineProperty(process, 'platform', { value: 'win32', configurable: true })
// ... 검증
})
it('비동기 메서드', async () => {
await expect(service.fetch()).resolves.toEqual(...)
await expect(service.fail()).rejects.toThrow('reason')
})
UI 컴포넌트 테스트가 아니라면 snapshot 보다 명시적 assertion 권장. snapshot 은 갱신 비용이 큼.
$ npm test
...
File | % Stmts | % Branch | % Funcs | % Lines |
---------------|---------|----------|---------|---------|
All files | 85.2 | 78.4 | 91.3 | 85.2 |
src/main/foo | 72.4 | 65.0 | 80.0 | 72.4 |
src/main/bar | 45.1 | 30.0 | 60.0 | 45.1 | ← 70% 미달 → 추가 테스트
신규 모듈로 전체 커버리지를 떨어뜨리지 말 것 (vitest.config thresholds 가 실패 시키지만, 그 전에 본인이 확인).