jest-testing
Automatically activated when user works with Jest tests, mentions Jest configuration, asks about Jest matchers/mocks, or has files matching *.test.js, *.test.ts, jest.config.*. Provides Jest-specific expertise for testing React, Node.js, and JavaScript applications. Also applies to Vitest due to API compatibility. Does NOT handle general quality analysis - use analyzing-test-quality for that.
下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o jest-testing.zip https://jpskill.com/download/17691.zip && unzip -o jest-testing.zip && rm jest-testing.zip
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/17691.zip -OutFile "$d\jest-testing.zip"; Expand-Archive "$d\jest-testing.zip" -DestinationPath $d -Force; ri "$d\jest-testing.zip"
完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。
💾 手動でダウンロードしたい(コマンドが難しい人向け)
- 1. 下の青いボタンを押して
jest-testing.zipをダウンロード - 2. ZIPファイルをダブルクリックで解凍 →
jest-testingフォルダができる - 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-18
- 取得日時
- 2026-05-18
- 同梱ファイル
- 4
📖 Skill本文(日本語訳)
※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。
Jest テストのエキスパート
あなたは Jest テストフレームワークのエキスパートであり、その設定、マッチャー、モック、そして JavaScript および TypeScript アプリケーションのテストにおけるベストプラクティスについて深い知識を持っています。
あなたの能力
- Jest の設定: セットアップ、設定ファイル、環境、およびプリセット
- マッチャーとアサーション: 組み込みおよびカスタムマッチャー、非対称マッチャー
- モック: モック関数、モジュール、タイマー、および外部依存関係
- スナップショットテスト: インラインおよび外部スナップショット、スナップショットの更新
- コードカバレッジ: カバレッジ設定、閾値、およびレポート
- テストの構成:
describeブロック、フック、テストフィルタリング - React のテスト: Jest DOM および RTL を使用した React コンポーネントのテスト
このスキルを使用するタイミング
Claude は、以下の場合にこのスキルを自動的に呼び出す必要があります。
- ユーザーが Jest、
jest.config、または Jest 固有の機能について言及した場合 *.test.js、*.test.ts、*.test.jsx、*.test.tsxに一致するファイルが見つかった場合- ユーザーがモック、スナップショット、または Jest マッチャーについて質問した場合
- 会話に React、Node.js、または JavaScript アプリのテストが含まれる場合
- Jest の設定またはセットアップについて議論されている場合
このスキルの使用方法
リソースへのアクセス
{baseDir} を使用して、このスキルディレクトリ内のファイルを参照します。
- スクリプト:
{baseDir}/scripts/ - ドキュメント:
{baseDir}/references/ - テンプレート:
{baseDir}/assets/
プログレッシブディスカバリー
- コアとなる Jest の専門知識から始めます
- 必要に応じて、特定のドキュメントを参照します
- テンプレートからコード例を提供します
利用可能なリソース
このスキルには、{baseDir} にすぐに使用できるリソースが含まれています。
- references/jest-cheatsheet.md - マッチャー、モック、非同期パターン、および CLI コマンドのクイックリファレンス
- assets/test-file.template.ts - ユニットテスト、非同期テスト、クラステスト、モックテスト、React コンポーネント、およびフック用の完全なテストテンプレート
- scripts/check-jest-setup.sh - Jest の設定と依存関係を検証します
Jest のベストプラクティス
テスト構造
describe('ComponentName', () => {
beforeEach(() => {
// Setup
});
afterEach(() => {
// Cleanup
});
describe('method or behavior', () => {
it('should do expected thing when condition', () => {
// Arrange
// Act
// Assert
});
});
});
モックパターン
モック関数
const mockFn = jest.fn();
mockFn.mockReturnValue('value');
mockFn.mockResolvedValue('async value');
mockFn.mockImplementation((arg) => arg * 2);
モックモジュール
jest.mock('./module', () => ({
func: jest.fn().mockReturnValue('mocked'),
}));
モックタイマー
jest.useFakeTimers();
jest.advanceTimersByTime(1000);
jest.runAllTimers();
一般的なマッチャー
expect(value).toBe(expected); // 厳密な等価性
expect(value).toEqual(expected); // 深い等価性
expect(value).toBeTruthy(); // Truthy
expect(value).toContain(item); // 配列/文字列に含む
expect(fn).toHaveBeenCalledWith(args); // 関数が引数付きで呼び出された
expect(value).toMatchSnapshot(); // スナップショット
expect(fn).toThrow(error); // 例外をスローする
非同期テスト
// Promises
it('async test', async () => {
await expect(asyncFn()).resolves.toBe('value');
});
// Callbacks
it('callback test', (done) => {
callbackFn((result) => {
expect(result).toBe('value');
done();
});
});
Jest の設定
基本的な設定
// jest.config.js
module.exports = {
testEnvironment: 'node', // or 'jsdom'
roots: ['<rootDir>/src'],
testMatch: ['**/__tests__/**/*.ts', '**/*.test.ts'],
transform: {
'^.+\\.tsx?$': 'ts-jest',
},
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
},
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80,
},
},
};
React Testing Library
カスタムレンダーによるセットアップ
// test-utils.tsx
import { render, RenderOptions } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { BrowserRouter } from 'react-router-dom';
const AllProviders = ({ children }: { children: React.ReactNode }) => {
const queryClient = new QueryClient({
defaultOptions: { queries: { retry: false } },
});
return (
<QueryClientProvider client={queryClient}>
<BrowserRouter>
{children}
</BrowserRouter>
</QueryClientProvider>
);
};
export const renderWithProviders = (
ui: React.ReactElement,
options?: RenderOptions
) => render(ui, { wrapper: AllProviders, ...options });
export * from '@testing-library/react';
クエリの優先順位 (良い順から悪い順)
// 1. アクセシブルなクエリ (最良)
screen.getByRole('button', { name: 'Submit' });
screen.getByLabelText('Email');
screen.getByPlaceholderText('Enter email');
screen.getByText('Welcome');
// 2. セマンティックなクエリ
screen.getByAltText('Profile picture');
screen.getByTitle('Close');
// 3. テスト ID (最後の手段)
screen.getByTestId('submit-button');
ユーザーインタラクション
import userEvent from '@testing-library/user-event';
test('form submission', async () => {
const user = userEvent.setup();
render(<LoginForm />);
// Type in inputs
await user.type(screen.getByLabelText('Email'), 'test@example.com');
await user.type(screen.getByLabelText('Password'), 'password123');
// Click button
await user.click(screen.getByRole('button', { name: 'Sign in' }));
// Check result
await waitFor(() => {
expect(screen.getByText('Welcome!')).toBeInTheDocument();
});
});
test('keyboard navigation', async () => {
const user = userEvent.setup();
render(<Form />);
await user.tab(); // Focus first element
await user.keyboard('{Enter}'); // Press enter
await user.keyboard('[ShiftLeft>][Tab][/ShiftLeft]'); // Shift+Tab
});
フックのテスト
import { renderHook, act } from '@testing-library/react';
import { useCounter } from './us 📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開
Jest Testing Expertise
You are an expert in Jest testing framework with deep knowledge of its configuration, matchers, mocks, and best practices for testing JavaScript and TypeScript applications.
Your Capabilities
- Jest Configuration: Setup, configuration files, environments, and presets
- Matchers & Assertions: Built-in and custom matchers, asymmetric matchers
- Mocking: Mock functions, modules, timers, and external dependencies
- Snapshot Testing: Inline and external snapshots, snapshot updates
- Code Coverage: Coverage configuration, thresholds, and reports
- Test Organization: Describe blocks, hooks, test filtering
- React Testing: Testing React components with Jest DOM and RTL
When to Use This Skill
Claude should automatically invoke this skill when:
- The user mentions Jest, jest.config, or Jest-specific features
- Files matching
*.test.js,*.test.ts,*.test.jsx,*.test.tsxare encountered - The user asks about mocking, snapshots, or Jest matchers
- The conversation involves testing React, Node.js, or JavaScript apps
- Jest configuration or setup is discussed
How to Use This Skill
Accessing Resources
Use {baseDir} to reference files in this skill directory:
- Scripts:
{baseDir}/scripts/ - Documentation:
{baseDir}/references/ - Templates:
{baseDir}/assets/
Progressive Discovery
- Start with core Jest expertise
- Reference specific documentation as needed
- Provide code examples from templates
Available Resources
This skill includes ready-to-use resources in {baseDir}:
- references/jest-cheatsheet.md - Quick reference for matchers, mocks, async patterns, and CLI commands
- assets/test-file.template.ts - Complete test templates for unit tests, async tests, class tests, mock tests, React components, and hooks
- scripts/check-jest-setup.sh - Validates Jest configuration and dependencies
Jest Best Practices
Test Structure
describe('ComponentName', () => {
beforeEach(() => {
// Setup
});
afterEach(() => {
// Cleanup
});
describe('method or behavior', () => {
it('should do expected thing when condition', () => {
// Arrange
// Act
// Assert
});
});
});
Mocking Patterns
Mock Functions
const mockFn = jest.fn();
mockFn.mockReturnValue('value');
mockFn.mockResolvedValue('async value');
mockFn.mockImplementation((arg) => arg * 2);
Mock Modules
jest.mock('./module', () => ({
func: jest.fn().mockReturnValue('mocked'),
}));
Mock Timers
jest.useFakeTimers();
jest.advanceTimersByTime(1000);
jest.runAllTimers();
Common Matchers
expect(value).toBe(expected); // Strict equality
expect(value).toEqual(expected); // Deep equality
expect(value).toBeTruthy(); // Truthy
expect(value).toContain(item); // Array/string contains
expect(fn).toHaveBeenCalledWith(args); // Function called with
expect(value).toMatchSnapshot(); // Snapshot
expect(fn).toThrow(error); // Throws
Async Testing
// Promises
it('async test', async () => {
await expect(asyncFn()).resolves.toBe('value');
});
// Callbacks
it('callback test', (done) => {
callbackFn((result) => {
expect(result).toBe('value');
done();
});
});
Jest Configuration
Basic Configuration
// jest.config.js
module.exports = {
testEnvironment: 'node', // or 'jsdom'
roots: ['<rootDir>/src'],
testMatch: ['**/__tests__/**/*.ts', '**/*.test.ts'],
transform: {
'^.+\\.tsx?$': 'ts-jest',
},
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
},
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80,
},
},
};
React Testing Library
Setup with Custom Render
// test-utils.tsx
import { render, RenderOptions } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { BrowserRouter } from 'react-router-dom';
const AllProviders = ({ children }: { children: React.ReactNode }) => {
const queryClient = new QueryClient({
defaultOptions: { queries: { retry: false } },
});
return (
<QueryClientProvider client={queryClient}>
<BrowserRouter>
{children}
</BrowserRouter>
</QueryClientProvider>
);
};
export const renderWithProviders = (
ui: React.ReactElement,
options?: RenderOptions
) => render(ui, { wrapper: AllProviders, ...options });
export * from '@testing-library/react';
Query Priority (Best to Worst)
// 1. Accessible queries (best)
screen.getByRole('button', { name: 'Submit' });
screen.getByLabelText('Email');
screen.getByPlaceholderText('Enter email');
screen.getByText('Welcome');
// 2. Semantic queries
screen.getByAltText('Profile picture');
screen.getByTitle('Close');
// 3. Test IDs (last resort)
screen.getByTestId('submit-button');
User Interactions
import userEvent from '@testing-library/user-event';
test('form submission', async () => {
const user = userEvent.setup();
render(<LoginForm />);
// Type in inputs
await user.type(screen.getByLabelText('Email'), 'test@example.com');
await user.type(screen.getByLabelText('Password'), 'password123');
// Click button
await user.click(screen.getByRole('button', { name: 'Sign in' }));
// Check result
await waitFor(() => {
expect(screen.getByText('Welcome!')).toBeInTheDocument();
});
});
test('keyboard navigation', async () => {
const user = userEvent.setup();
render(<Form />);
await user.tab(); // Focus first element
await user.keyboard('{Enter}'); // Press enter
await user.keyboard('[ShiftLeft>][Tab][/ShiftLeft]'); // Shift+Tab
});
Testing Hooks
import { renderHook, act } from '@testing-library/react';
import { useCounter } from './useCounter';
test('useCounter increments', () => {
const { result } = renderHook(() => useCounter());
expect(result.current.count).toBe(0);
act(() => {
result.current.increment();
});
expect(result.current.count).toBe(1);
});
// With wrapper for context
test('hook with context', () => {
const wrapper = ({ children }) => (
<ThemeProvider theme="dark">{children}</ThemeProvider>
);
const { result } = renderHook(() => useTheme(), { wrapper });
expect(result.current.theme).toBe('dark');
});
Async Assertions
import { waitFor, waitForElementToBeRemoved } from '@testing-library/react';
test('async loading', async () => {
render(<DataFetcher />);
// Wait for loading to disappear
await waitForElementToBeRemoved(() => screen.queryByText('Loading...'));
// Wait for content
await waitFor(() => {
expect(screen.getByText('Data loaded')).toBeInTheDocument();
});
// With timeout
await waitFor(
() => expect(screen.getByText('Slow content')).toBeInTheDocument(),
{ timeout: 5000 }
);
});
Network Mocking with MSW
Setup
// src/mocks/handlers.ts
import { http, HttpResponse } from 'msw';
export const handlers = [
http.get('/api/users', () => {
return HttpResponse.json([
{ id: 1, name: 'John' },
{ id: 2, name: 'Jane' },
]);
}),
http.post('/api/users', async ({ request }) => {
const body = await request.json();
return HttpResponse.json({ id: 3, ...body }, { status: 201 });
}),
http.delete('/api/users/:id', ({ params }) => {
return HttpResponse.json({ deleted: params.id });
}),
];
// src/mocks/server.ts
import { setupServer } from 'msw/node';
import { handlers } from './handlers';
export const server = setupServer(...handlers);
Jest Setup
// jest.setup.ts
import { server } from './src/mocks/server';
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
Test-Specific Handlers
import { server } from '../mocks/server';
import { http, HttpResponse } from 'msw';
test('handles error response', async () => {
// Override for this test only
server.use(
http.get('/api/users', () => {
return HttpResponse.json(
{ error: 'Server error' },
{ status: 500 }
);
})
);
render(<UserList />);
await waitFor(() => {
expect(screen.getByText('Failed to load users')).toBeInTheDocument();
});
});
test('handles network error', async () => {
server.use(
http.get('/api/users', () => {
return HttpResponse.error();
})
);
render(<UserList />);
await waitFor(() => {
expect(screen.getByText('Network error')).toBeInTheDocument();
});
});
Request Assertions
test('sends correct request', async () => {
let capturedRequest: Request | null = null;
server.use(
http.post('/api/users', async ({ request }) => {
capturedRequest = request.clone();
return HttpResponse.json({ id: 1 });
})
);
render(<CreateUserForm />);
await userEvent.type(screen.getByLabelText('Name'), 'John');
await userEvent.click(screen.getByRole('button', { name: 'Create' }));
await waitFor(() => {
expect(capturedRequest).not.toBeNull();
});
const body = await capturedRequest!.json();
expect(body).toEqual({ name: 'John' });
});
Custom Matchers
Creating Custom Matchers
// jest.setup.ts
expect.extend({
toBeWithinRange(received: number, floor: number, ceiling: number) {
const pass = received >= floor && received <= ceiling;
return {
pass,
message: () =>
pass
? `expected ${received} not to be within range ${floor} - ${ceiling}`
: `expected ${received} to be within range ${floor} - ${ceiling}`,
};
},
toHaveBeenCalledOnceWith(received: jest.Mock, ...args: unknown[]) {
const pass =
received.mock.calls.length === 1 &&
JSON.stringify(received.mock.calls[0]) === JSON.stringify(args);
return {
pass,
message: () =>
pass
? `expected not to be called once with ${args}`
: `expected to be called once with ${args}, but was called ${received.mock.calls.length} times`,
};
},
});
// Type declarations
declare global {
namespace jest {
interface Matchers<R> {
toBeWithinRange(floor: number, ceiling: number): R;
toHaveBeenCalledOnceWith(...args: unknown[]): R;
}
}
}
Asymmetric Matchers
test('asymmetric matchers', () => {
const data = {
id: 123,
name: 'Test',
createdAt: new Date().toISOString(),
};
expect(data).toEqual({
id: expect.any(Number),
name: expect.stringContaining('Test'),
createdAt: expect.stringMatching(/^\d{4}-\d{2}-\d{2}/),
});
expect(['a', 'b', 'c']).toEqual(
expect.arrayContaining(['a', 'c'])
);
expect({ a: 1, b: 2, c: 3 }).toEqual(
expect.objectContaining({ a: 1, b: 2 })
);
});
Debugging Jest Tests
Debug Output
import { screen } from '@testing-library/react';
test('debugging', () => {
render(<MyComponent />);
// Print DOM
screen.debug();
// Print specific element
screen.debug(screen.getByRole('button'));
// Get readable DOM
console.log(prettyDOM(container));
});
Finding Slow Tests
# Run with verbose timing
jest --verbose
# Detect open handles
jest --detectOpenHandles
# Run tests serially to find interactions
jest --runInBand
Common Debug Patterns
// Check what's in the DOM
test('debug queries', () => {
render(<MyComponent />);
// Log all available roles
screen.getByRole(''); // Will error with available roles
// Check accessible name
screen.logTestingPlaygroundURL(); // Opens playground
});
// Debug async issues
test('async debug', async () => {
render(<AsyncComponent />);
// Use findBy for async elements
const element = await screen.findByText('Loaded');
// Log state at each step
screen.debug();
});
CI/CD Integration
GitHub Actions Workflow
# .github/workflows/test.yml
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test -- --coverage --ci
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
files: ./coverage/lcov.info
Jest CI Configuration
// jest.config.js
module.exports = {
// ... other config
// CI-specific settings
...(process.env.CI && {
maxWorkers: 2,
ci: true,
coverageReporters: ['lcov', 'text-summary'],
}),
// Coverage thresholds
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80,
},
},
};
Caching Dependencies
# In GitHub Actions
- name: Cache Jest
uses: actions/cache@v3
with:
path: |
node_modules/.cache/jest
key: jest-${{ runner.os }}-${{ hashFiles('**/jest.config.js') }}
Common Issues & Solutions
Issue: Tests are slow
- Use
jest.mock()for expensive modules - Run tests in parallel with
--maxWorkers - Use
beforeAllfor expensive setup - Mock network requests with MSW
Issue: Flaky tests
- Mock timers for timing-dependent code
- Use
waitForfor async state changes - Avoid shared mutable state
- Use
findByqueries for async elements
Issue: Mock not working
- Ensure mock is before import
- Use
jest.resetModules()between tests - Check module path matches exactly
- Use
jest.doMock()for dynamic mocks
Issue: Memory leaks
- Clean up in
afterEach - Mock timers with
jest.useFakeTimers() - Use
--detectLeaksflag - Check for unresolved promises
Examples
Example 1: Testing a React Component
When testing React components:
- Check for React Testing Library usage
- Verify proper queries (getByRole, getByLabelText)
- Test user interactions with userEvent
- Assert on accessible elements
Example 2: Testing API Calls
When testing code that makes API calls:
- Mock fetch or axios at module level
- Test success and error scenarios
- Verify request parameters
- Test loading states
Version Compatibility
The patterns in this skill require the following minimum versions:
| Package | Minimum Version | Features Used |
|---|---|---|
| Jest | 29.0+ | Modern mock APIs, ESM support |
| @testing-library/react | 14.0+ | renderHook in main package |
| @testing-library/user-event | 14.0+ | userEvent.setup() API |
| msw | 2.0+ | http, HttpResponse (v1 used rest, ctx) |
| @testing-library/jest-dom | 6.0+ | Modern matchers |
Migration Notes
MSW v1 → v2:
// v1 (deprecated)
import { rest } from 'msw';
rest.get('/api', (req, res, ctx) => res(ctx.json(data)));
// v2 (current)
import { http, HttpResponse } from 'msw';
http.get('/api', () => HttpResponse.json(data));
user-event v13 → v14:
// v13 (deprecated)
userEvent.click(button);
// v14 (current)
const user = userEvent.setup();
await user.click(button);
Important Notes
- Jest is automatically invoked by Claude when relevant
- Always check for jest.config.js/ts for project-specific settings
- Use
{baseDir}variable to reference skill resources - Prefer Testing Library queries over direct DOM access for React
同梱ファイル
※ ZIPに含まれるファイル一覧。`SKILL.md` 本体に加え、参考資料・サンプル・スクリプトが入っている場合があります。
- 📄 SKILL.md (16,378 bytes)
- 📎 assets/test-file.template.ts (6,735 bytes)
- 📎 references/jest-cheatsheet.md (6,545 bytes)
- 📎 scripts/check-jest-setup.sh (5,563 bytes)