jest-vitest
JavaScriptやTypeScriptのテストコード作成、Reactコンポーネントのテスト、モック設定など、幅広いテスト関連作業を支援するSkill。
📜 元の英語説明(参考)
JavaScript/TypeScript testing with Jest and Vitest. Use when user asks to "write JS tests", "add unit tests", "test React component", "mock a module", "snapshot testing", "test async code", "set up Jest", "migrate to Vitest", or any JS/TS testing tasks.
🇯🇵 日本人クリエイター向け解説
JavaScriptやTypeScriptのテストコード作成、Reactコンポーネントのテスト、モック設定など、幅広いテスト関連作業を支援するSkill。
※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。
下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o jest-vitest.zip https://jpskill.com/download/6096.zip && unzip -o jest-vitest.zip && rm jest-vitest.zip
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/6096.zip -OutFile "$d\jest-vitest.zip"; Expand-Archive "$d\jest-vitest.zip" -DestinationPath $d -Force; ri "$d\jest-vitest.zip"
完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。
💾 手動でダウンロードしたい(コマンドが難しい人向け)
- 1. 下の青いボタンを押して
jest-vitest.zipをダウンロード - 2. ZIPファイルをダブルクリックで解凍 →
jest-vitestフォルダができる - 3. そのフォルダを
C:\Users\あなたの名前\.claude\skills\(Win)または~/.claude/skills/(Mac)へ移動 - 4. Claude Code を再起動
⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。
🎯 このSkillでできること
下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。
📦 インストール方法 (3ステップ)
- 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
- 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
- 3. 展開してできたフォルダを、ホームフォルダの
.claude/skills/に置く- · macOS / Linux:
~/.claude/skills/ - · Windows:
%USERPROFILE%\.claude\skills\
- · macOS / Linux:
Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。
詳しい使い方ガイドを見る →- 最終更新
- 2026-05-17
- 取得日時
- 2026-05-17
- 同梱ファイル
- 1
📖 Skill本文(日本語訳)
※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。
Jest & Vitest
JestとVitestを使ったJavaScript/TypeScriptのテストです。
テストの実行
# Jest
npx jest
npx jest --watch
npx jest --coverage
npx jest path/to/test.ts
npx jest -t "test name pattern"
# Vitest
npx vitest
npx vitest run # Single run (no watch)
npx vitest --coverage
npx vitest path/to/test.ts
テストの構造
// JestとVitestは同じAPIを共有しています
describe("Calculator", () => {
let calc: Calculator;
beforeEach(() => {
calc = new Calculator();
});
afterEach(() => {
calc.reset();
});
it("should add two numbers", () => {
expect(calc.add(2, 3)).toBe(5);
});
it("should throw on division by zero", () => {
expect(() => calc.divide(1, 0)).toThrow("Division by zero");
});
describe("negative numbers", () => {
it("should handle negative addition", () => {
expect(calc.add(-1, -2)).toBe(-3);
});
});
});
一般的なマッチャー
// 同等性
expect(value).toBe(exact); // === 厳密
expect(value).toEqual(deepEqual); // ディープ同等性
expect(value).toStrictEqual(strict); // ディープ + 型
// 真偽値
expect(value).toBeTruthy();
expect(value).toBeFalsy();
expect(value).toBeNull();
expect(value).toBeUndefined();
expect(value).toBeDefined();
// 数値
expect(value).toBeGreaterThan(3);
expect(value).toBeLessThanOrEqual(10);
expect(value).toBeCloseTo(0.3, 5);
// 文字列
expect(str).toMatch(/regex/);
expect(str).toContain("substring");
// 配列/オブジェクト
expect(arr).toContain(item);
expect(arr).toHaveLength(3);
expect(obj).toHaveProperty("key", "value");
expect(obj).toMatchObject({ name: "Alice" });
// 例外
expect(() => fn()).toThrow();
expect(() => fn()).toThrow("message");
expect(() => fn()).toThrow(ErrorClass);
モック
// モック関数
const mockFn = jest.fn(); // Jest
const mockFn = vi.fn(); // Vitest
mockFn.mockReturnValue(42);
mockFn.mockReturnValueOnce(1);
mockFn.mockResolvedValue({ data: [] });
mockFn.mockImplementation((x) => x * 2);
// アサーション
expect(mockFn).toHaveBeenCalled();
expect(mockFn).toHaveBeenCalledTimes(2);
expect(mockFn).toHaveBeenCalledWith("arg1", "arg2");
// モックモジュール
jest.mock("./database"); // Jest
vi.mock("./database"); // Vitest
// 実装付きモック
jest.mock("./api", () => ({
fetchUser: jest.fn().mockResolvedValue({ name: "Alice" }),
}));
// Vitestでの同等な記述
vi.mock("./api", () => ({
fetchUser: vi.fn().mockResolvedValue({ name: "Alice" }),
}));
// 既存のメソッドをスパイ
const spy = jest.spyOn(object, "method"); // Jest
const spy = vi.spyOn(object, "method"); // Vitest
// モックタイマー
jest.useFakeTimers(); // Jest
vi.useFakeTimers(); // Vitest
jest.advanceTimersByTime(1000); // Jest
vi.advanceTimersByTime(1000); // Vitest
非同期テスト
// Async/await
it("fetches users", async () => {
const users = await fetchUsers();
expect(users).toHaveLength(3);
});
// Resolved/rejected
it("resolves with data", async () => {
await expect(fetchData()).resolves.toEqual({ ok: true });
});
it("rejects with error", async () => {
await expect(fetchBad()).rejects.toThrow("Network error");
});
スナップショットテスト
// スナップショットの作成/更新
it("renders correctly", () => {
const tree = render(<Button label="Click" />);
expect(tree).toMatchSnapshot();
});
// インラインスナップショット
it("serializes", () => {
expect(serialize(data)).toMatchInlineSnapshot(`"expected output"`);
});
// スナップショットの更新: jest --updateSnapshot / vitest --update
Reactテスト (Testing Library)
import { render, screen, fireEvent, waitFor } from "@testing-library/react";
it("renders and interacts", async () => {
render(<LoginForm onSubmit={mockSubmit} />);
// 要素のクエリ
const input = screen.getByRole("textbox", { name: /email/i });
const button = screen.getByRole("button", { name: /submit/i });
// 操作
fireEvent.change(input, { target: { value: "alice@test.com" } });
fireEvent.click(button);
// アサート
await waitFor(() => {
expect(mockSubmit).toHaveBeenCalledWith("alice@test.com");
});
expect(screen.getByText("Success")).toBeInTheDocument();
});
// userEvent (より現実的)
import userEvent from "@testing-library/user-event";
it("types and submits", async () => {
const user = userEvent.setup();
render(<Form />);
await user.type(screen.getByRole("textbox"), "hello");
await user.click(screen.getByRole("button"));
});
カバレッジ
# Jest
npx jest --coverage --coverageThreshold='{"global":{"branches":80,"functions":80,"lines":80}}'
# Vitest (vitest.config.ts)
# coverage: { provider: "v8", thresholds: { lines: 80 } }
設定
// vitest.config.ts
import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
globals: true,
environment: "jsdom",
setupFiles: ["./tests/setup.ts"],
coverage: {
provider: "v8",
reporter: ["text", "html"],
exclude: ["node_modules/", "tests/"],
},
},
});
// jest.config.js
module.exports = {
preset: "ts-jest",
testEnvironment: "jsdom",
setupFilesAfterSetup: ["./tests/setup.ts"],
moduleNameMapper: {
"^@/(.*)$": "<rootDir>/src/$1",
},
coverageThreshold: {
global: { branches: 80, functions: 80, lines: 80, statements: 80 },
},
};
参照
高度なパターン、Reactテストのレシピ、移行ガイドについては、references/patterns.mdをご覧ください。
📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開
Jest & Vitest
JavaScript/TypeScript testing with Jest and Vitest.
Running Tests
# Jest
npx jest
npx jest --watch
npx jest --coverage
npx jest path/to/test.ts
npx jest -t "test name pattern"
# Vitest
npx vitest
npx vitest run # Single run (no watch)
npx vitest --coverage
npx vitest path/to/test.ts
Test Structure
// Jest and Vitest share the same API
describe("Calculator", () => {
let calc: Calculator;
beforeEach(() => {
calc = new Calculator();
});
afterEach(() => {
calc.reset();
});
it("should add two numbers", () => {
expect(calc.add(2, 3)).toBe(5);
});
it("should throw on division by zero", () => {
expect(() => calc.divide(1, 0)).toThrow("Division by zero");
});
describe("negative numbers", () => {
it("should handle negative addition", () => {
expect(calc.add(-1, -2)).toBe(-3);
});
});
});
Common Matchers
// Equality
expect(value).toBe(exact); // === strict
expect(value).toEqual(deepEqual); // Deep equality
expect(value).toStrictEqual(strict); // Deep + type
// Truthiness
expect(value).toBeTruthy();
expect(value).toBeFalsy();
expect(value).toBeNull();
expect(value).toBeUndefined();
expect(value).toBeDefined();
// Numbers
expect(value).toBeGreaterThan(3);
expect(value).toBeLessThanOrEqual(10);
expect(value).toBeCloseTo(0.3, 5);
// Strings
expect(str).toMatch(/regex/);
expect(str).toContain("substring");
// Arrays/Objects
expect(arr).toContain(item);
expect(arr).toHaveLength(3);
expect(obj).toHaveProperty("key", "value");
expect(obj).toMatchObject({ name: "Alice" });
// Exceptions
expect(() => fn()).toThrow();
expect(() => fn()).toThrow("message");
expect(() => fn()).toThrow(ErrorClass);
Mocking
// Mock function
const mockFn = jest.fn(); // Jest
const mockFn = vi.fn(); // Vitest
mockFn.mockReturnValue(42);
mockFn.mockReturnValueOnce(1);
mockFn.mockResolvedValue({ data: [] });
mockFn.mockImplementation((x) => x * 2);
// Assertions
expect(mockFn).toHaveBeenCalled();
expect(mockFn).toHaveBeenCalledTimes(2);
expect(mockFn).toHaveBeenCalledWith("arg1", "arg2");
// Mock module
jest.mock("./database"); // Jest
vi.mock("./database"); // Vitest
// Mock with implementation
jest.mock("./api", () => ({
fetchUser: jest.fn().mockResolvedValue({ name: "Alice" }),
}));
// Vitest equivalent
vi.mock("./api", () => ({
fetchUser: vi.fn().mockResolvedValue({ name: "Alice" }),
}));
// Spy on existing method
const spy = jest.spyOn(object, "method"); // Jest
const spy = vi.spyOn(object, "method"); // Vitest
// Mock timers
jest.useFakeTimers(); // Jest
vi.useFakeTimers(); // Vitest
jest.advanceTimersByTime(1000); // Jest
vi.advanceTimersByTime(1000); // Vitest
Async Testing
// Async/await
it("fetches users", async () => {
const users = await fetchUsers();
expect(users).toHaveLength(3);
});
// Resolved/rejected
it("resolves with data", async () => {
await expect(fetchData()).resolves.toEqual({ ok: true });
});
it("rejects with error", async () => {
await expect(fetchBad()).rejects.toThrow("Network error");
});
Snapshot Testing
// Create/update snapshot
it("renders correctly", () => {
const tree = render(<Button label="Click" />);
expect(tree).toMatchSnapshot();
});
// Inline snapshot
it("serializes", () => {
expect(serialize(data)).toMatchInlineSnapshot(`"expected output"`);
});
// Update snapshots: jest --updateSnapshot / vitest --update
React Testing (Testing Library)
import { render, screen, fireEvent, waitFor } from "@testing-library/react";
it("renders and interacts", async () => {
render(<LoginForm onSubmit={mockSubmit} />);
// Query elements
const input = screen.getByRole("textbox", { name: /email/i });
const button = screen.getByRole("button", { name: /submit/i });
// Interact
fireEvent.change(input, { target: { value: "alice@test.com" } });
fireEvent.click(button);
// Assert
await waitFor(() => {
expect(mockSubmit).toHaveBeenCalledWith("alice@test.com");
});
expect(screen.getByText("Success")).toBeInTheDocument();
});
// userEvent (more realistic)
import userEvent from "@testing-library/user-event";
it("types and submits", async () => {
const user = userEvent.setup();
render(<Form />);
await user.type(screen.getByRole("textbox"), "hello");
await user.click(screen.getByRole("button"));
});
Coverage
# Jest
npx jest --coverage --coverageThreshold='{"global":{"branches":80,"functions":80,"lines":80}}'
# Vitest (vitest.config.ts)
# coverage: { provider: "v8", thresholds: { lines: 80 } }
Configuration
// vitest.config.ts
import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
globals: true,
environment: "jsdom",
setupFiles: ["./tests/setup.ts"],
coverage: {
provider: "v8",
reporter: ["text", "html"],
exclude: ["node_modules/", "tests/"],
},
},
});
// jest.config.js
module.exports = {
preset: "ts-jest",
testEnvironment: "jsdom",
setupFilesAfterSetup: ["./tests/setup.ts"],
moduleNameMapper: {
"^@/(.*)$": "<rootDir>/src/$1",
},
coverageThreshold: {
global: { branches: 80, functions: 80, lines: 80, statements: 80 },
},
};
Reference
For advanced patterns, React testing recipes, and migration guides: references/patterns.md