بنقرة واحدة
vitest
Vitest 4.x 測試框架最佳實踐指南。當需要設定測試、撰寫單元/元件測試、mocking、coverage、整合 Astro/Three.js 測試時使用。
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
Vitest 4.x 測試框架最佳實踐指南。當需要設定測試、撰寫單元/元件測試、mocking、coverage、整合 Astro/Three.js 測試時使用。
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
Zod v4 schema validation 最佳實踐指南。當需要定義 schema、驗證/解析 JSON 資料、type inference、或處理 unknown data 時使用。
Svelte 5 + Astro 整合最佳實踐指南。當需要建立 Svelte 元件、使用 runes API、整合 Astro islands、或用 Testing Library 測試 Svelte 元件時使用。
GitHub GraphQL API 最佳實踐指南。當需要使用 GraphQL 查詢使用者資料、處理 cursor pagination、計算 rate limit、或除錯 GraphQL errors 時使用。
gayanvoice/top-github-users 架構參考指南。當需要了解 GitHub 使用者排行榜的資料抓取管線、國家設定、排行計算邏輯、已知問題、或社群需求時使用。
Commander.js v14 CLI 框架最佳實踐。當需要建立 CLI 工具、解析命令列參數、設計 subcommands 時使用。
GitHub Actions CI/CD 最佳實踐指南。當需要設定 workflow、cron 排程、GitHub Pages 部署、使用 Octokit API、或處理 rate limiting 時使用。
| name | vitest |
| description | Vitest 4.x 測試框架最佳實踐指南。當需要設定測試、撰寫單元/元件測試、mocking、coverage、整合 Astro/Three.js 測試時使用。 |
toMatchScreenshot()、expect.schemaMatching、expect.assertnpm install -D vitest @vitest/coverage-v8
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
environment: 'node',
globals: true,
include: ['src/**/*.{test,spec}.{ts,tsx}'],
exclude: ['node_modules', 'dist', 'e2e'],
restoreMocks: true, // 自動還原 mock
pool: 'forks', // 預設,最安全
coverage: {
provider: 'v8',
include: ['src/**/*.ts'],
exclude: ['src/**/*.d.ts', 'src/**/*.test.ts'],
thresholds: { statements: 80, branches: 80, functions: 80, lines: 80 },
},
},
});
import { getViteConfig } from 'astro/config';
export default getViteConfig({
test: { /* vitest options */ },
});
import { experimental_AstroContainer as AstroContainer } from 'astro/container';
import Card from '../src/components/Card.astro';
test('Card renders', async () => {
const container = await AstroContainer.create();
const html = await container.renderToString(Card, {
slots: { default: 'content' },
});
expect(html).toContain('content');
});
const handler = vi.fn();
handler('arg');
expect(handler).toHaveBeenCalledWith('arg');
const spy = vi.spyOn(console, 'log');
doSomething();
expect(spy).toHaveBeenCalledWith('expected');
vi.mock('./api', () => ({
fetchData: vi.fn().mockResolvedValue({ id: 1 }),
}));
| 方法 | 效果 |
|---|---|
mockClear() | 清除呼叫記錄 |
mockReset() | 清除 + 移除實作 |
mockRestore() | 還原原始(僅 spy) |
建議:設定 restoreMocks: true 自動還原。
it('fetches data', async () => {
const data = await fetchUser(1);
expect(data.name).toBe('Alice');
});
it('debounce', () => {
vi.useFakeTimers();
const fn = vi.fn();
debounce(fn, 300)();
vi.advanceTimersByTime(300);
expect(fn).toHaveBeenCalledOnce();
vi.useRealTimers();
});
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
ok: true,
json: () => Promise.resolve({ data: 'test' }),
}));
WebGL 在 Node.js 不可用,策略:
vi.mock('three', async (importOriginal) => {
const actual = await importOriginal<typeof import('three')>();
return {
...actual,
WebGLRenderer: vi.fn().mockImplementation(() => ({
setSize: vi.fn(),
render: vi.fn(),
domElement: document.createElement('canvas'),
dispose: vi.fn(),
setPixelRatio: vi.fn(),
})),
};
});
原則:將場景邏輯(物件位置、層級、材質)與渲染分開測試。Mock WebGLRenderer,專注測試 scene graph。
vi.mock('node:fs/promises', () => ({
readFile: vi.fn().mockResolvedValue('content'),
writeFile: vi.fn().mockResolvedValue(undefined),
}));
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
ok: true,
json: () => Promise.resolve([{ id: 1 }]),
}));
import { collectData } from './collect';
it('collects data', async () => {
const result = await collectData();
expect(result).toHaveLength(1);
});
export default defineConfig({
test: {
projects: [
{
extends: true,
test: { name: 'unit', include: ['src/**/*.unit.test.ts'], environment: 'node' },
},
{
extends: true,
test: { name: 'components', include: ['src/**/*.component.test.ts'], environment: 'jsdom' },
},
],
},
});
執行特定 project:vitest --project unit
| v8(預設) | Istanbul | |
|---|---|---|
| 速度 | 較快 | 較慢 |
| 安裝 | @vitest/coverage-v8 | @vitest/coverage-istanbul |
| 適用 | V8 runtime(Node.js) | 所有 JS runtime |
建議用 v8。忽略特定行:/* v8 ignore next */
// 單檔覆蓋
// @vitest-environment happy-dom
expect(result).toMatchSnapshot(); // 檔案 snapshot
expect(result).toMatchInlineSnapshot(); // inline(自動填入)
expect(html).toMatchFileSnapshot('./expected.html'); // 自訂檔案
更新:vitest -u
export function add(a: number, b: number) { return a + b; }
if (import.meta.vitest) {
const { it, expect } = import.meta.vitest;
it('adds', () => expect(add(1, 2)).toBe(3));
}
設定:includeSource: ['src/**/*.ts']
Production 移除:define: { 'import.meta.vitest': 'undefined' }
| 策略 | 做法 |
|---|---|
| Pool | threads(大專案)、forks(預設,安全) |
| 停用隔離 | isolate: false(無副作用時) |
| 限縮搜尋 | dir: 'src' |
| CI 分片 | --shard=1/4 + --merge-reports |
forksforksvite-tsconfig-pathsexpect().rejects 處理預期錯誤檔名:*.test-d.ts,執行:vitest --typecheck
import { expectTypeOf } from 'vitest';
expectTypeOf(add).parameter(0).toBeNumber();
expectTypeOf(add(1, 2)).toEqualTypeOf<number>();