mcpField
T
test-coverage

test-coverage

github.com/cognitedataテスト・品質61.9k 回インストール1 日前に更新
リポジトリ

test-coverage について

test-coverageは、Flowsアプリのテストカバレッジ不足を調査し、修正するスキルです。行カバレッジ80%の基準に向けて、設定確認、テスト作成、未カバー箇所の補完、テスト容易性の改善を案内します。

主な機能

  1. 01行カバレッジ80%の基準
  2. 02VitestとJestのカバレッジ設定確認
  3. 03本番コードの測定範囲確認
  4. 04テスト追加と不足箇所の修正ガイド
  5. 05テスト容易性のレビューとリファクタリング

活用シーン

  1. 01Flowsアプリのカバレッジ基準への対応
  2. 02テストのない本番ファイルの特定
  3. 03未カバーの分岐やエラー経路へのテスト追加
  4. 04依存関係のリファクタリングによるテスト容易性向上

インストール

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

$npx skills add https://github.com/cognitedata/builder-skills --skill test-coverage

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

詳細

テストカバレッジの修正

$ARGUMENTS(引数が指定されていない場合はアプリ全体)のテストカバレッジを修正します。このスキルでは、カバレッジの不足箇所を見つけて修正し、Flows アプリの承認に必須の80%の行カバレッジというハードゲートを満たします。すべての手順を順番に進めてください。


ステップ 1 — テストフレームワークとカバレッジツールを確認する

プロジェクトに、カバレッジが設定された動作するテストフレームワークがあることを確認します。

# Check for vitest or jest in package.json
grep -E "(vitest|jest)" package.json

# Check for coverage configuration
cat vitest.config.ts 2>/dev/null || cat vitest.config.js 2>/dev/null || cat jest.config.ts 2>/dev/null || cat jest.config.js 2>/dev/null

次の点を確認してください。

  • テストフレームワーク(Vitest または Jest)がインストールされ、設定されていること
  • 設定ファイルに coverage セクションがあること(例:vitest.config.ts の coverage: { provider: 'v8', ... })
  • カバレッジレポーターが設定されていること(少なくとも text と lcov または json-summary)

カバレッジツールが設定されていない場合は、今すぐ設定してください。

  1. カバレッジプロバイダーをインストールします。
pnpm add -D @vitest/coverage-v8
  1. vitest.config.ts にカバレッジ設定を追加します。既存の設定ファイルを読み込み、test の中に coverage セクションを追加してください。
// vitest.config.ts — minimum coverage configuration to add
test: {
  coverage: {
    provider: 'v8',
    reporter: ['text', 'text-summary', 'lcov'],
    include: ['src/**/*.{ts,tsx}'],
    exclude: [
      'src/**/*.test.{ts,tsx}',
      'src/**/*.spec.{ts,tsx}',
      'src/**/vite-env.d.ts',
      'src/main.tsx',
    ],
  },
}

更新した設定ファイルを書き込みます。vitest.config.ts が存在しない場合は、defineConfig のラッパーを含むファイルを新規作成してください。


ステップ 2 — カバレッジの対象範囲を検証する

80%のしきい値は、src/ 以下のすべての .ts および .tsx ファイルに適用されます。ただし、除外できるのは次のファイルのみです。

  • テストファイル(*.test.ts、*.test.tsx、*.spec.ts、*.spec.tsx)
  • 型宣言ファイル(vite-env.d.ts)
  • エントリーポイント(main.tsx)

アプリでは、ページ、コンポーネント、フック、その他の本番コードをカバレッジ測定から除外してはなりません。

# Check what files are excluded from coverage in the config
grep -A 20 "exclude" vitest.config.ts 2>/dev/null || grep -A 20 "exclude" vitest.config.js 2>/dev/null

# Check for coveragePathIgnorePatterns in jest config
grep -A 10 "coveragePathIgnorePatterns\|collectCoverageFrom" jest.config.ts 2>/dev/null

本番ファイルが設定で除外されている場合は、今すぐ修正してください。

本番コードをカバレッジ測定から除外する設定を削除します。除外できるのは、テストファイル、型宣言ファイル、エントリーポイントのみです。exclude 配列を次のものだけを含むように書き換えてください。

exclude: [
  'src/**/*.test.{ts,tsx}',
  'src/**/*.spec.{ts,tsx}',
  'src/**/vite-env.d.ts',
  'src/main.tsx',
],

次の除外設定は必ず削除してください。

  • src/pages/、src/components/、src/hooks/ — 許可されません
  • 特定の機能ファイル — 生成コードでない限り許可されません
  • src/**/*.tsx(すべてのコンポーネント)— 許可されません。これはアプリの大部分を隠してしまいます。

修正した設定ファイルを書き込みます。


ステップ 3 — テストを実行してカバレッジを収集する

# Try common coverage commands based on project setup
npx vitest run --coverage 2>/dev/null || npx jest --coverage 2>/dev/null || npm test -- --coverage 2>/dev/null

カバレッジの概要を記録します。

  • ステートメント: X%
  • 分岐: X%
  • 関数: X%
  • 行: X%

ハードゲート: 全体の行カバレッジは80%以上である必要があります。このしきい値を下回るアプリは、要修正として扱われます。

テストが実行できない場合は、今すぐ修正してください。

よくある修正方法:

  • インポート漏れ: 失敗したテストファイルを読み込み、不足している import 文を追加して、修正したファイルを書き込みます。
  • モックの不整合: テストを読んで、何がモックされているかを確認します。モック対象のモジュールの現在の API に合わせてモックを修正します。
  • 古いスナップショット: npx vitest run --update を実行してスナップショットを更新し、差分を確認して正しいことを確かめます。
  • 依存関係の不足: まだインストールされていないテストユーティリティがあれば、pnpm add -D <missing-package> を実行します。
  • 設定エラー: 設定ファイルを読み込み、構文またはオプションの誤りを修正して、修正したファイルを書き込みます。

すべてのテストが通るまで、修正のたびにテストを再実行してください。その後、カバレッジの概要を記録します。


Step 4 — 不足しているテストファイルを見つけて作成する

src/ 配下にある重要な .ts/.tsx ファイルごとに、対応するテストファイルがあるか確認します。

# List all production files and check for test counterparts
for file in $(find src -name "*.ts" -o -name "*.tsx" | grep -v ".test." | grep -v ".spec." | grep -v "node_modules" | grep -v "vite-env" | sort); do
  base="${file%.*}"
  ext="${file##*.}"
  dir=$(dirname "$file")
  filename=$(basename "$base")

  # Check for test file in same directory or __tests__ directory
  test_exists="false"
  for pattern in "${base}.test.${ext}" "${base}.spec.${ext}" "${base}.test.ts" "${base}.spec.ts" "${dir}/__tests__/${filename}.test.${ext}" "${dir}/__tests__/${filename}.spec.${ext}"; do
    if [ -f "$pattern" ]; then
      test_exists="true"
      break
    fi
  done

  if [ "$test_exists" = "false" ]; then
    echo "NO TEST: $file"
  fi
done

テストがない各ファイルを分類します:

  • サービス、フック、ユーティリティ、コンテキスト、ViewModel フック — 今すぐテストファイルを作成します(以下を参照)
  • ロジックのない純粋なプレゼンテーションコンポーネント — N/A とします(テスト不要)
  • バレルエクスポート(再エクスポートのみを行う index.ts)— N/A とします
  • 型のみのファイル(.d.ts、または型/インターフェースのエクスポートのみを含むファイル)— N/A とします

テストがないファイルごとに、包括的なテストファイルを作成します。 本番コードが対応している場合は、依存関係のモックにコンテキストインジェクションを使用します。本番コードがハードコードされたインポートを使用している場合は、テスト容易性に関する懸念として記録したうえで、理由を示すコメントを付けて vi.mock を使ったテストを作成します。ファイルごとに、次の手順に従います:

  1. ソースファイルを読み込み、 エクスポート、依存関係、ロジックを把握します。
  2. ソースファイルと同じディレクトリに .test.ts または .test.tsx ファイルを作成します。
  3. 次のケースを網羅するテストを記述します: 正常系、エラー系、空の状態、エッジケース。

ファイルの種類に適したテストパターンを使用します:

フックの場合:

  • @testing-library/react の renderHook を使ってテストします
  • 必要なプロバイダー(QueryClientProvider、カスタムコンテキストプロバイダーなど)でラップします
  • 初期状態、読み込み状態、成功状態、エラー状態をテストします
  • 構造の例:
import { renderHook, waitFor } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
// import the hook

const createWrapper = () => {
  const queryClient = new QueryClient({
    defaultOptions: { queries: { retry: false } },
  });
  return ({ children }: { children: React.ReactNode }) => (
    <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
  );
};

describe('useMyHook', () => {
  it('returns data on success', async () => {
    const { result } = renderHook(() => useMyHook(), { wrapper: createWrapper() });
    await waitFor(() => expect(result.current.data).toBeDefined());
  });

  it('handles errors', async () => {
    // set up error condition
    const { result } = renderHook(() => useMyHook(), { wrapper: createWrapper() });
    await waitFor(() => expect(result.current.error).toBeDefined());
  });
});

サービス/ユーティリティの場合:

  • 関数を直接呼び出してテストします
  • 必要に応じて CDF SDK の応答をモックします
  • 戻り値、副作用、スローされるエラーをテストします
  • 構造の例:
import { describe, it, expect, vi } from 'vitest';
// import the service/util functions

describe('myService', () => {
  it('returns expected result for valid input', () => {
    const result = myFunction(validInput);
    expect(result).toEqual(expectedOutput);
  });

  it('throws on invalid input', () => {
    expect(() => myFunction(invalidInput)).toThrow();
  });
});

ロジックを含むコンポーネントの場合:

  • @testing-library/react の render を使ってテストします
  • 読み込み状態、エラー状態、データ状態を検証します
  • 状態変化を引き起こすユーザー操作をテストします
  • 構造の例:
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
// import the component and providers

describe('MyComponent', () => {
  it('shows loading state initially', () => {
    render(<MyComponent />, { wrapper: createWrapper() });
    expect(screen.getByText(/loading/i)).toBeInTheDocument();
  });

  it('renders data after loading', async () => {
    render(<MyComponent />, { wrapper: createWrapper() });
    await waitFor(() => {
      expect(screen.getByText('expected content')).toBeInTheDocument();
    });
  });
});

依存関係のモックに関するガイドライン:

  • 可能な場合は vi.mock ではなくコンテキストインジェクションを使用し、フックのコンテキスト経由でテスト用の依存関係を渡します。
  • 本番コードがコンテキストインジェクションを妨げるハードコードされたインポートを使用している場合は、理由を説明するコメントを付けて vi.mock を使用します(例:// vi.mock required: useDataSource uses direct import, not context injection)。
  • モックが型安全であることを確認します。as unknown as T によるキャストは使用しないでください。インターフェースを満たす適切なモックオブジェクトを定義します。

各テストファイルを作成したら、npx vitest run <test-file> を実行して、テストが成功することを確認します。


Step 5 — カバレッジの低いファイルを修正する

カバレッジツールがファイルごとの指標を出力する場合、行カバレッジが 80% 未満のファイルを一覧表示します:

# Parse lcov or text output for per-file coverage
cat coverage/coverage-summary.json 2>/dev/null | node -e "
  const data = JSON.parse(require('fs').readFileSync('/dev/stdin','utf8'));
  Object.entries(data).forEach(([file, metrics]) => {
    if (file === 'total') return;
    const pct = metrics.lines?.pct ?? 0;
    if (pct < 80) console.log(pct.toFixed(1) + '% — ' + file);
  });
" 2>/dev/null

カバレッジが 80% 未満のファイルごとに、カバレッジレポートで未カバー行を確認し、その特定のコードパスを実行するテストケースを追加します:

  1. カバレッジレポートで未カバー行を確認します。 coverage/ ディレクトリにある、未カバー行を示すファイルごとの詳細レポート(lcov または html)を確認します。
  2. ソースファイルを読み込み、 未カバー行が何を行うか把握します。
  3. 特定のコードパスを実行するテストケースを追加します:
  • 未カバーのエラーパス: エラー条件(ネットワーク障害、無効な入力、null 値、権限エラー)を発生させるテストを追加します。不正な入力を渡すか、依存関係が例外をスローするようにモックして、エラーを強制的に発生させます。
  • 未カバーの分岐: 条件分岐ごとにテストを追加します。if/else の true 分岐しかテストされていない場合は、false 分岐を実行するテストを記述します。
  • 未カバーの関数: カバレッジがないエクスポート関数を呼び出すテストを追加します。戻り値と副作用を検証します。
  • 未カバーの catch ブロック: 上流の呼び出しが reject/throw するようにモックし、catch ブロックの動作を検証します。
  1. テストを追加した後、カバレッジを再度実行し、ファイルが 80% に達したことを確認します:
npx vitest run --coverage <test-file>

各ファイルが少なくとも 80% の行カバレッジに達するか、実行可能なすべてのパスを網羅するまで繰り返します。


ステップ 6 — テスト容易性のパターンを修正する

コードベースのテスト容易性に関する問題を評価し、修正します。

# Check for dependency injection via context
grep -rn --include="*.ts" --include="*.tsx" "useContext\|createContext" src/hooks/ src/contexts/

# Check for vi.mock usage (red flag for testability)
grep -rn --include="*.ts" --include="*.tsx" "vi\.mock" src/

# Check for unsafe casts in tests
grep -rn --include="*.ts" --include="*.tsx" "as unknown as" src/ | grep -E "\.test\.|\.spec\."

# Check for interface-based services
grep -rn --include="*.ts" --include="*.tsx" -E "implements\s+\w+" src/

見つかったテスト容易性の問題ごとに、本番コードをリファクタリングして、より優れたテストパターンをサポートしてください。その後、対応するテストを更新して、改善されたパターンを使用してください。

問題修正
フックがコンテキストを使わず、依存関係を直接インポートしているデフォルトの依存関係を持つコンテキスト型を追加します。createContext でコンテキストを作成し、実際の実装を使用するデフォルト値を設定します。フックでは useContext を使用して依存関係を取得します。テストでは、vi.mock を使わず、コンテキストプロバイダーを介してモック依存関係を提供できます。
サービスにインターフェースがないサービスの公開 API を記述する TypeScript インターフェースを抽出します。クラスまたはオブジェクトにそのインターフェースを実装させます。テストでは具体的な実装ではなく、インターフェースを基にモックします。
ページコンポーネントでデータ取得とレンダリングが混在しているデータロジックを use*ViewModel フックに抽出します。ページコンポーネントでは ViewModel フックを呼び出し、その戻り値に基づいてレンダリングします。renderHook を使って ViewModel フックを個別にテストし、モックした ViewModel を使ってページコンポーネントをテストします。
コンテキスト注入で対応できるモジュールに対して、テストで vi.mock を使用している本番コードをリファクタリングしてコンテキスト注入を使うようにした後、コンテキストプロバイダーを介してモック依存関係を提供するようにテストを更新します。vi.mock の呼び出しを削除します。このパターンを説明するコメントを追加します。
テストでモックに as unknown as T キャストを使用している必要なインターフェースを満たす適切なモック型またはオブジェクトを定義します。キャストを適切に型付けされたモックに置き換えます。インターフェースが大きい場合は、使用するメソッドのみを含む部分モックを正しく型付けして返すヘルパー関数を作成します。

リファクタリングしたファイルごとに、次の手順を実行します。

  1. ソースファイルとそのテストファイルを読みます。
  2. 本番コードをリファクタリングします(コンテキストの追加、インターフェースの抽出、ViewModel フックの抽出)。
  3. 改善されたパターンを使うようにテストを更新します。
  4. npx vitest run <test-file> を実行して、テストが引き続き成功することを確認します。

ステップ 7 — 残りの不足点を報告する

カバレッジを含めてテストスイート全体を再実行し、最終的な数値を取得します。

npx vitest run --coverage 2>/dev/null || npx jest --coverage 2>/dev/null

実施した作業と残っている項目をまとめます。

### Test Coverage Summary (After Fixes)

| Metric | Before | After | Gate |
|--------|--------|-------|------|
| Lines | X% | Y% | ≥80% required |
| Branches | X% | Y% | — |
| Functions | X% | Y% | — |
| Statements | X% | Y% | — |

### Coverage verdict: PASS / FAIL

### What was fixed
- [ ] Coverage tooling configured/corrected
- [ ] Exclusions cleaned up (removed N production file exclusions)
- [ ] N failing tests fixed
- [ ] N new test files written (list them)
- [ ] N existing test files expanded for coverage
- [ ] N files refactored for testability

### Remaining gaps (needs human review)
Only list issues that could not be auto-fixed:
- Complex business logic where correct test assertions require domain knowledge
- Integration tests that need real API credentials or environment setup
- Files where coverage cannot reach 80% without major architectural changes (explain why)

完了

次の項目をまとめます。

  • 修正前と修正後の全体カバレッジと、80% の基準との比較(PASS または FAIL)
  • 作成したテストファイル数と追加したテスト数
  • テスト容易性のためにリファクタリングしたファイル数
  • 人によるレビューが必要な、残っている必須修正項目

コメント