mcpField
F
frontend-testing

frontend-testing

github.com/pageai-proテスト・品質33571 回インストール18 週間前に更新
リポジトリ

frontend-testing について

Claude が既存のプロジェクト規約に沿ってフロントエンドテストを作成・レビューするためのスキルです。ファイル単位の段階的な進め方、統合テストの指針、Vitest と React Testing Library の利用方法を扱います。

主な機能

  1. 01フロントエンドテストの作成とレビュー
  2. 02ファイル単位の段階的な手順
  3. 03統合テストの指針
  4. 04モックと非同期テストのパターン
  5. 05カバレッジとシナリオのチェックリスト

活用シーン

  1. 01React コンポーネントやフックのテスト作成
  2. 02フロントエンドテストの網羅性レビュー
  3. 03フロントエンドディレクトリのカバレッジ改善

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/pageai-pro/ralph-loop --skill frontend-testing

インストール後、Claude Code で/frontend-testingを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

assets/
component-test.template.tsx
hook-test.template.ts
utility-test.template.ts
references/
async-testing.md
checklist.md
common-patterns.md
mocking.md
workflow.md

詳細

このスキルを使用すると、確立された規約とベストプラクティスに従って、高品質で包括的なフロントエンドテストを Claude が作成できます。

このスキルを適用するタイミング

ユーザーが次のことを求めている場合に、このスキルを適用します。

  • コンポーネント、フック、ユーティリティのテスト作成
  • 既存のテストのレビューによる網羅性の確認
  • Vitest、React Testing Library、RTL、またはspec ファイルについて言及
  • テストカバレッジの改善
  • フロントエンドコードのテスト、ユニットテスト、または統合テストについて言及
  • フロントエンドのコードベースにおけるテストパターンの理解

次の場合は適用しないでください。

  • E2E テスト(Playwright)についての質問
  • コードの文脈がなく、概念的な質問のみの場合

クイックリファレンス

技術スタック
ツールバージョン目的
Vitest4+テストランナー
React Testing Library16+コンポーネントのテスト
jsdom-テスト環境
TypeScript5+型安全性

注:この一覧は、プロジェクトの依存関係に合わせて最新の状態に保ってください。

主なコマンド

フィードバックを早く得られるよう、テストスイート全体よりも特定のテストの実行を常に優先してください。

# Run ALL tests (avoid during development)
npx vitest run

# ✅ PREFERRED: Run specific file
npx vitest run src/components/Button.spec.tsx

# ✅ PREFERRED: Run tests matching a pattern
npx vitest run --grep "Button"
npx vitest run -t "should render"

# ✅ PREFERRED: Run specific describe block
npx vitest run --grep "Button > Rendering"

# ✅ Run tests in a directory
npx vitest run src/components/

# ✅ Run single test by name
npx vitest run -t "should disable button when loading"
ウォッチモード(バックグラウンドテスト)

効率的に反復開発を進めるには、ウォッチモードを使用します。

# ✅ Watch mode - reruns on file changes
npx vitest

# ✅ Watch specific file
npx vitest src/components/Button.spec.tsx

# ✅ Watch tests matching pattern
npx vitest --grep "Button"
ファイル名
  • テストファイル:ComponentName.spec.tsx(コンポーネントと同じディレクトリ)

テスト構成のテンプレート

import { render, screen, fireEvent, waitFor } from '@testing-library/react'
import Component from './index'

// ✅ Import real project components (DO NOT mock these)
// import Loading from '@/app/components/base/loading'
// import { ChildComponent } from './child-component'

// ✅ Mock external dependencies only
vi.mock('@/service/api')
vi.mock('next/navigation', () => ({
  useRouter: () => ({ push: vi.fn() }),
  usePathname: () => '/test',
}))

// Shared state for mocks (if needed)
let mockSharedState = false

describe('ComponentName', () => {
  beforeEach(() => {
    vi.clearAllMocks()  // ✅ Reset mocks BEFORE each test
    mockSharedState = false  // ✅ Reset shared state
  })

  // Rendering tests (REQUIRED)
  describe('Rendering', () => {
    it('should render without crashing', () => {
      // Arrange
      const props = { title: 'Test' }

      // Act
      render(<Component {...props} />)

      // Assert
      expect(screen.getByText('Test')).toBeInTheDocument()
    })
  })

  // Props tests (REQUIRED)
  describe('Props', () => {
    it('should apply custom className', () => {
      render(<Component className="custom" />)
      expect(screen.getByRole('button')).toHaveClass('custom')
    })
  })

  // User Interactions
  describe('User Interactions', () => {
    it('should handle click events', () => {
      const handleClick = vi.fn()
      render(<Component onClick={handleClick} />)

      fireEvent.click(screen.getByRole('button'))

      expect(handleClick).toHaveBeenCalledTimes(1)
    })
  })

  // Edge Cases (REQUIRED)
  describe('Edge Cases', () => {
    it('should handle null data', () => {
      render(<Component data={null} />)
      expect(screen.getByText(/no data/i)).toBeInTheDocument()
    })

    it('should handle empty array', () => {
      render(<Component items={[]} />)
      expect(screen.getByText(/empty/i)).toBeInTheDocument()
    })
  })
})

テストワークフロー(重要)

⚠️ 段階的なアプローチが必須

すべてのテストファイルを一度に生成してはいけません。 複雑なコンポーネントや複数ファイルを含むディレクトリの場合:

  1. 分析と計画:すべてのファイルを列挙し、複雑さの順(単純 → 複雑)に並べます
  2. 一度に1つずつ処理:テストを作成 → テストを実行 → 必要に応じて修正 → 次へ
  3. 次に進む前に検証:現在のファイルのテストが成功するまで、次のファイルに進まないでください
For each file:
  ┌────────────────────────────────────────────────┐
  │ 1. Write test                                  │
  │ 2. Run: npm run test-unit <file>.spec.tsx      │
  │ 3. PASS? → Mark complete, next file            │
  │    FAIL? → Fix first, then continue            │
  └────────────────────────────────────────────────┘
複雑さに基づく順序

複数ファイルをテストする場合は、次の順で処理します。

  1. 🟢 ユーティリティ関数(最も単純)
  2. 🟢 カスタムフック
  3. 🟡 シンプルなコンポーネント(プレゼンテーション用)
  4. 🟡 中程度のコンポーネント(状態、エフェクト)
  5. 🔴 複雑なコンポーネント(API、ルーティング)
  6. 🔴 統合テスト(index ファイルは最後)
先にリファクタリングする場合
  • 中程度の複雑さ:テストの前に、より小さな部分に分割します
  • 500 行以上:テストの前に分割を検討します
  • 依存関係が多い:先にロジックをフックに抽出します

テスト戦略

パス単位のテスト(ディレクトリのテスト)

ディレクトリまたはパスのテストを依頼された場合、そのパス内のすべての内容をテストします。

  • ディレクトリ内のすべてのコンポーネント、フック、ユーティリティをテストします(index ファイルだけではありません)
  • 段階的なアプローチを使用します。1 ファイルずつ処理し、次に進む前にそれぞれを検証します
  • 目標:ディレクトリ内のすべてのファイルで 100% のカバレッジを達成します
統合テストを優先

ディレクトリのテストを作成する場合は、統合テストを優先します。

  • ✅ 実際のプロジェクトコンポーネントを直接インポートします(ベースコンポーネントや兄弟コンポーネントを含む)
  • ✅ モックする対象は次のものに限定します:API サービス(@/service/*)、next/navigation、複雑なコンテキストプロバイダー
  • ❌ ベースコンポーネント(@/app/components/base/*)をモックしないでください
  • ❌ 同じディレクトリ内の兄弟コンポーネントや子コンポーネントをモックしないでください

基本原則

1. AAA パターン(Arrange-Act-Assert)

各テストは、次の要素を明確に分けます。

  • Arrange:テストデータを準備し、コンポーネントをレンダリングします
  • Act:ユーザー操作を実行します
  • Assert:期待される結果を検証します
2. ブラックボックステスト
  • 実装の詳細ではなく、観察可能な動作をテストします
  • セマンティッククエリ(getByRole、getByLabelText)を使用します
  • 内部状態を直接テストすることは避けます
  • アサーションでは、ハードコードした文字列よりもパターンマッチングを優先します。
// ❌ Avoid: hardcoded text assertions
expect(screen.getByText('Loading...')).toBeInTheDocument()

// ✅ Better: role-based queries
expect(screen.getByRole('status')).toBeInTheDocument()

// ✅ Better: pattern matching
expect(screen.getByText(/loading/i)).toBeInTheDocument()
3. テストごとに1つの動作

各テストでは、ユーザーから観察できる動作を1つだけ検証します。

// ✅ Good: One behavior
it('should disable button when loading', () => {
  render(<Button loading />)
  expect(screen.getByRole('button')).toBeDisabled()
})

// ❌ Bad: Multiple behaviors
it('should handle loading state', () => {
  render(<Button loading />)
  expect(screen.getByRole('button')).toBeDisabled()
  expect(screen.getByText('Loading...')).toBeInTheDocument()
  expect(screen.getByRole('button')).toHaveClass('loading')
})
4. セマンティックな命名

should <behavior> when <condition> を使用します。

it('should show error message when validation fails')
it('should call onSubmit when form is valid')
it('should disable input when isReadOnly is true')

必須のテストシナリオ

常に必須(すべてのコンポーネント)
  1. レンダリング:コンポーネントがクラッシュせずにレンダリングされること
  2. Props:必須の props、任意の props、デフォルト値
  3. エッジケース:null、undefined、空の値、境界条件
条件付き(該当する場合)
機能テストの重点項目
useState初期状態、遷移、クリーンアップ
useEffect実行、依存関係、クリーンアップ
イベントハンドラーすべての onClick、onChange、onSubmit、キーボード
API 呼び出し読み込み、成功、エラーの各状態
ルーティングナビゲーション、パラメーター、クエリー文字列
useCallback/useMemo参照の同一性
コンテキストプロバイダーの値、コンシューマーの動作
フォームバリデーション、送信、エラーの表示

カバレッジ目標(ファイルごと)

生成するテストファイルごとに、以下を目指してください。

  • ✅ 100% の関数カバレッジ
  • ✅ 100% のステートメントカバレッジ
  • ✅ >95% のブランチカバレッジ
  • ✅ >95% の行カバレッジ

注:複数ファイルからなるディレクトリでは、ファイルを1つずつ処理し、それぞれで完全なカバレッジを達成してください。references/workflow.md を参照してください。

詳細ガイド

詳細については、以下を参照してください。

  • references/workflow.md - 段階的なテストのワークフロー(複数ファイルのテストでは必読)
  • references/mocking.md - モックのパターンとベストプラクティス
  • references/async-testing.md - 非同期処理と API 呼び出し
  • references/common-patterns.md - よく使われるテストパターン
  • references/checklist.md - テスト生成チェックリストと検証手順
プロジェクト設定
  • vitest.config.ts - Vitest の設定
  • vitest.setup.ts - テスト環境のセットアップ
  • モジュールは自動的にはモックされません。グローバルモックは vitest.setup.ts に記述されています(例:react-i18next、next/image)。ky や mime などの他のモジュールは、テストファイル内でローカルにモックしてください。

コメント