.claude/skills-ja/frontend-typescript-testing/SKILL.md
React Testing Library、MSW、Playwright E2Eでテストを設計。コンポーネントテストとE2Eテストパターンを適用。
npx skillsauth add shinpr/ai-coding-project-boilerplate frontend-typescript-testingInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
import { describe, it, expect, beforeEach, vi } from 'vitest'import { render, screen } from '@testing-library/react'import userEvent from '@testing-library/user-event'vi.mock() を使用必須: 単体テストのカバレッジは60%以上 コンポーネント別目標:
指標: Statements(文)、Branches(分岐)、Functions(関数)、Lines(行)
単体テスト(React Testing Library)
統合テスト(React Testing Library + MSW)
E2Eテストでの機能横断検証
src/
└── components/
└── Button/
├── Button.tsx
├── Button.test.tsx # コンポーネントと同じ場所に配置
└── index.ts
理由:
{ComponentName}.test.tsx{FeatureName}.integration.test.tsx推奨: すべてのテストを常に有効に保つ
避けるべき: test.skip()やコメントアウト
// 型安全なMSWハンドラー(MSW v2)
import { http, HttpResponse } from 'msw'
const handlers = [
http.get('/api/users/:id', () => {
return HttpResponse.json({ id: '1', name: 'John' } satisfies User)
})
]
// 必要な部分のみ
type TestProps = Pick<ButtonProps, 'label' | 'onClick'>
const mockProps: TestProps = { label: 'Click', onClick: vi.fn() }
// やむを得ない場合のみ、理由明記
const mockRouter = {
push: vi.fn()
} as unknown as Router // 複雑なRouter型構造のため
import { describe, it, expect, vi } from 'vitest'
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { Button } from './Button'
describe('Button', () => {
it('should call onClick when clicked', async () => {
const user = userEvent.setup()
const onClick = vi.fn()
render(<Button label="Click me" onClick={onClick} />)
await user.click(screen.getByRole('button', { name: 'Click me' }))
expect(onClick).toHaveBeenCalledOnce()
})
})
正常系に加え、境界値と異常系を含める。
it('renders empty state for empty array', () => {
render(<UserList users={[]} />)
expect(screen.getByText('ユーザーがいません')).toBeInTheDocument()
})
it('displays error message on API failure', async () => {
server.use(rest.get('/api/users', (req, res, ctx) => res(ctx.status(500))))
render(<UserList />)
expect(await screen.findByText('エラーが発生しました')).toBeInTheDocument()
})
// 良い: アクセシブルなクエリ
screen.getByRole('button', { name: 'Submit' })
screen.getByLabelText('Email')
screen.getByText('Welcome')
// 悪い: 実装詳細への依存
screen.getByTestId('submit-btn')
container.querySelector('.btn-primary')
it('loads and displays user data', async () => {
render(<UserProfile userId="1" />)
// ローディング状態を確認
expect(screen.getByText('Loading...')).toBeInTheDocument()
// データ表示を待機
expect(await screen.findByText('John Doe')).toBeInTheDocument()
// ローディングが消えていることを確認
expect(screen.queryByText('Loading...')).not.toBeInTheDocument()
})
it('submits form with valid data', async () => {
const onSubmit = vi.fn()
render(<LoginForm onSubmit={onSubmit} />)
await userEvent.type(screen.getByLabelText('Email'), '[email protected]')
await userEvent.type(screen.getByLabelText('Password'), 'password123')
await userEvent.click(screen.getByRole('button', { name: 'Login' }))
expect(onSubmit).toHaveBeenCalledWith({
email: '[email protected]',
password: 'password123'
})
})
// Correct: test user-visible results
it('increments count when clicked', async () => {
const user = userEvent.setup()
render(<Counter />)
await user.click(screen.getByRole('button', { name: '+' }))
expect(screen.getByText('Count: 1')).toBeInTheDocument()
})
// Avoid: testing implementation details
it('calls setState', () => {
const setState = vi.spyOn(React, 'useState')
render(<Counter />)
// ...
})
development
Vitestテスト設計と品質基準を適用。カバレッジ要件とモック使用ガイドを提供。ユニットテスト作成時に使用。
development
型安全性とエラーハンドリングルールを適用。any禁止、型ガード必須。TypeScript実装、型定義レビュー時に使用。
tools
環境変数、アーキテクチャ設計、ビルド・テストコマンドを定義。環境設定、アーキテクチャ設計時に使用。
tools
タスクの本質を分析し適切なスキルを選択。規模見積もりとメタデータを返却。タスク開始時、スキル選択時に使用。