mcpField
T
test-specialist

test-specialist

github.com/ailabs-393テスト・品質460902 回インストール50 週間前に更新
リポジトリ

test-specialist について

JavaScript/TypeScript アプリケーション向けのテストスキルです。テスト設計、体系的なデバッグ、カバレッジ分析に加え、未テストのファイルを見つけるスクリプトを備えています。

主な機能

  1. 01単体・統合・E2E テストの設計
  2. 02体系的なバグ再現と根本原因分析
  3. 03セキュリティ・性能・境界値の確認
  4. 04カバレッジと未テストファイルの分析スクリプト
  5. 05テスト保守と品質の実践

活用シーン

  1. 01TDD で新機能のテストを作成
  2. 02バグを再現し、修正と回帰を検証
  3. 03カバレッジ不足や未テストのファイルを特定

インストール

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

$npx skills add https://github.com/ailabs-393/ai-labs-claude-skills --skill test-specialist

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

同梱ファイル

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

index.js
package.json
references/
bug_analysis.md
testing_patterns.md
scripts/
analyze_coverage.py
find_untested_code.py

詳細

テストスペシャリスト

概要

JavaScript/TypeScript アプリケーションに体系的なテスト手法とデバッグ技術を適用します。このスキルでは、テスト戦略、バグ分析のフレームワーク、カバレッジの不足や未テストのコードを特定する自動化ツールを包括的に提供します。

主な機能

1. テストケースの作成

ユニットテスト、統合テスト、エンドツーエンドテストの各シナリオを網羅するテストを作成します。

ユニットテストの手法

AAA パターン(Arrange-Act-Assert)を使ってテストを構成します。

describe('ExpenseCalculator', () => {
  describe('calculateTotal', () => {
    test('sums expense amounts correctly', () => {
      // Arrange
      const expenses = [
        { amount: 100, category: 'food' },
        { amount: 50, category: 'transport' },
        { amount: 25, category: 'entertainment' }
      ];

      // Act
      const total = calculateTotal(expenses);

      // Assert
      expect(total).toBe(175);
    });

    test('handles empty expense list', () => {
      expect(calculateTotal([])).toBe(0);
    });

    test('handles negative amounts', () => {
      const expenses = [
        { amount: 100, category: 'food' },
        { amount: -50, category: 'refund' }
      ];
      expect(calculateTotal(expenses)).toBe(50);
    });
  });
});

主な原則:

  • 1つのテストで1つの振る舞いを検証します
  • 正常系、エッジケース、エラー条件を網羅します
  • シナリオを説明する明確なテスト名を付けます
  • テストは独立させ、分離します
統合テストの手法

データベース、API、サービス間のやり取りなど、コンポーネントが連携して動作することをテストします。

describe('ExpenseAPI Integration', () => {
  beforeAll(async () => {
    await database.connect(TEST_DB_URL);
  });

  afterAll(async () => {
    await database.disconnect();
  });

  beforeEach(async () => {
    await database.clear();
    await seedTestData();
  });

  test('POST /expenses creates expense and updates total', async () => {
    const response = await request(app)
      .post('/api/expenses')
      .send({
        amount: 50,
        category: 'food',
        description: 'Lunch'
      })
      .expect(201);

    expect(response.body).toMatchObject({
      id: expect.any(Number),
      amount: 50,
      category: 'food'
    });

    // Verify database state
    const total = await getTotalExpenses();
    expect(total).toBe(50);
  });
});
エンドツーエンドテストの手法

Playwright や Cypress などのツールを使って、ユーザーのワークフロー全体をテストします。

test('user can track expense from start to finish', async ({ page }) => {
  // Navigate to app
  await page.goto('/');

  // Add new expense
  await page.click('[data-testid="add-expense-btn"]');
  await page.fill('[data-testid="amount"]', '50.00');
  await page.selectOption('[data-testid="category"]', 'food');
  await page.fill('[data-testid="description"]', 'Lunch');
  await page.click('[data-testid="submit"]');

  // Verify expense appears in list
  await expect(page.locator('[data-testid="expense-item"]')).toContainText('Lunch');
  await expect(page.locator('[data-testid="total"]')).toContainText('$50.00');
});
2. 体系的なバグ分析

構造化されたデバッグ手法を適用して、問題を特定し、修正します。

5段階の分析プロセス
  1. 再現:バグを確実に再現します

    • 発生させる正確な手順を記録します
    • 必要な環境や状態を特定します
    • 期待される動作と実際の動作を記録します
  2. 切り分け:問題の範囲を絞り込みます

    • コードパスを二分探索します
    • 最小限の再現ケースを作成します
    • 無関係な依存関係を取り除きます
  3. 根本原因分析:根本的な原因を特定します

    • 実行フローを追跡します
    • 前提条件や事前条件を確認します
    • 最近の変更を確認します(git blame)
  4. 修正の実装:解決策を実装します

    • まず失敗するテストを作成します(TDD)
    • 修正を実装します
    • テストが成功することを確認します
  5. 検証:問題が完全に解決されたことを確認します

    • テストスイート全体を実行します
    • エッジケースをテストします
    • リグレッションがないことを確認します
よくあるバグのパターン

競合状態:

// Test concurrent operations
test('handles concurrent updates correctly', async () => {
  const promises = Array.from({ length: 100 }, () =>
    incrementExpenseCount()
  );

  await Promise.all(promises);
  expect(getExpenseCount()).toBe(100);
});

Null/Undefined エラー:

// Test null safety
test.each([null, undefined, '', 0, false])
  ('handles invalid input: %p', (input) => {
    expect(() => processExpense(input)).toThrow('Invalid expense');
  });

オフバイワンエラー:

// Test boundaries explicitly
describe('pagination', () => {
  test('handles empty list', () => {
    expect(paginate([], 1, 10)).toEqual([]);
  });

  test('handles single item', () => {
    expect(paginate([item], 1, 10)).toEqual([item]);
  });

  test('handles last page with partial items', () => {
    const items = Array.from({ length: 25 }, (_, i) => i);
    expect(paginate(items, 3, 10)).toHaveLength(5);
  });
});
3. 潜在的な問題の特定

バグになる前に、問題を積極的に特定します。

セキュリティ脆弱性

よくあるセキュリティ上の問題をテストします。

describe('security', () => {
  test('prevents SQL injection', async () => {
    const malicious = "'; DROP TABLE expenses; --";
    await expect(
      searchExpenses(malicious)
    ).resolves.not.toThrow();
  });

  test('sanitizes XSS in descriptions', () => {
    const xss = '<script>alert("xss")</script>';
    const expense = createExpense({ description: xss });
    expect(expense.description).not.toContain('<script>');
  });

  test('requires authentication for expense operations', async () => {
    await request(app)
      .post('/api/expenses')
      .send({ amount: 50 })
      .expect(401);
  });
});
パフォーマンスの問題

パフォーマンス上の問題をテストします。

test('processes large expense list efficiently', () => {
  const largeList = Array.from({ length: 10000 }, (_, i) => ({
    amount: i,
    category: 'test'
  }));

  const start = performance.now();
  const total = calculateTotal(largeList);
  const duration = performance.now() - start;

  expect(duration).toBeLessThan(100); // Should complete in <100ms
  expect(total).toBe(49995000);
});
ロジックエラー

パラメーター化テストを使って、エッジケースを検出します。

test.each([
  // [input, expected, description]
  [[10, 20, 30], 60, 'normal positive values'],
  [[0, 0, 0], 0, 'all zeros'],
  [[-10, 20, -5], 5, 'mixed positive and negative'],
  [[0.1, 0.2], 0.3, 'decimal precision'],
  [[Number.MAX_SAFE_INTEGER], Number.MAX_SAFE_INTEGER, 'large numbers'],
])('calculateTotal(%p) = %p (%s)', (amounts, expected, description) => {
  const expenses = amounts.map(amount => ({ amount, category: 'test' }));
  expect(calculateTotal(expenses)).toBeCloseTo(expected);
});
4. テストカバレッジの分析

自動化ツールを使って、テストカバレッジの不足を特定します。

未テストのコードを見つける

付属のスクリプトを実行して、テストファイルがないソースファイルを特定します。

python3 scripts/find_untested_code.py src

このスクリプトは次の処理を行います。

  • ソースディレクトリ内のすべてのコードファイルをスキャンします
  • 対応するテストファイルがないファイルを特定します
  • 未テストのファイルを種類別に分類します(コンポーネント、サービス、ユーティリティなど)
  • 優先的にテストする必要があるファイルを判定します

結果の見方:

  • API/サービス:優先度が高い — ビジネスロジックとデータ操作をテストします
  • モデル:優先度が高い — データの検証と変換をテストします
  • フック:優先度は中程度 — 状態を持つ振る舞いをテストします
  • コンポーネント:優先度は中程度 — 複雑な UI ロジックをテストします
  • ユーティリティ:優先度は低い — 複雑な関数では必要に応じてテストします
カバレッジレポートの分析

カバレッジを生成した後、カバレッジ分析スクリプトを実行します。

# Generate coverage (using Jest example)
npm test -- --coverage

# Analyze coverage gaps
python3 scripts/analyze_coverage.py coverage/coverage-final.json

このスクリプトは次の項目を特定します。

  • カバレッジのしきい値(デフォルトは80%)を下回るファイル
  • ステートメント、分岐、関数のカバレッジ率
  • 改善を優先すべきファイル

カバレッジの目標:

  • 重要なパス:90%以上
  • ビジネスロジック:85%以上
  • UI コンポーネント:75%以上
  • ユーティリティ:70%以上
5. テストのメンテナンスと品質

テストの価値と保守性を維持します。

テストコードの品質原則

DRY(同じことを繰り返さない):

// Extract common setup
function createTestExpense(overrides = {}) {
  return {
    amount: 50,
    category: 'food',
    description: 'Test expense',
    date: new Date('2024-01-01'),
    ...overrides
  };
}

test('filters by category', () => {
  const expenses = [
    createTestExpense({ category: 'food' }),
    createTestExpense({ category: 'transport' }),
  ];
  // ...
});

明確なテストデータ:

// Bad: Magic numbers
expect(calculateDiscount(100, 0.15)).toBe(85);

// Good: Named constants
const ORIGINAL_PRICE = 100;
const DISCOUNT_RATE = 0.15;
const EXPECTED_PRICE = 85;
expect(calculateDiscount(ORIGINAL_PRICE, DISCOUNT_RATE)).toBe(EXPECTED_PRICE);

テスト間の依存を避ける:

// Bad: Tests depend on execution order
let sharedState;
test('test 1', () => {
  sharedState = { value: 1 };
});
test('test 2', () => {
  expect(sharedState.value).toBe(1); // Depends on test 1
});

// Good: Independent tests
test('test 1', () => {
  const state = { value: 1 };
  expect(state.value).toBe(1);
});
test('test 2', () => {
  const state = { value: 1 };
  expect(state.value).toBe(1);
});

ワークフローの判断ツリー

この判断ツリーに従って、テスト方法を決定してください。

  1. 新しい機能を追加しますか?

    • はい → まずテストを作成します(TDD)
      • 失敗するテストを書く
      • 機能を実装する
      • テストが成功することを確認する
      • リファクタリングする
    • いいえ → ステップ 2 に進む
  2. バグを修正しますか?

    • はい → バグ分析プロセスを適用する
      • バグを再現する
      • バグを示す失敗するテストを書く
      • 実装を修正する
      • テストが成功することを確認する
    • いいえ → ステップ 3 に進む
  3. テストカバレッジを改善しますか?

    • はい → カバレッジツールを使用する
      • find_untested_code.py を実行して不足箇所を特定する
      • カバレッジレポートに対して analyze_coverage.py を実行する
      • 重要なパスを優先する
      • 未テストのコードに対するテストを書く
    • いいえ → ステップ 4 に進む
  4. コード品質を分析しますか?

    • はい → 体系的にレビューする
      • セキュリティの脆弱性を確認する
      • エッジケースとエラー処理をテストする
      • パフォーマンス特性を確認する
      • エラー処理をレビューする

テストのフレームワークとツール

推奨スタック

ユニットテスト/統合テスト:

  • テストランナーには Jest または Vitest
  • React コンポーネントには Testing Library
  • API テストには Supertest
  • API のモックには MSW(Mock Service Worker)

E2E テスト:

  • Playwright または Cypress
  • ページオブジェクトモデルパターン

カバレッジ:

  • Istanbul(Jest/Vitest に組み込み)
  • JSON 形式のカバレッジレポート
テストの実行
# Run all tests
npm test

# Run with coverage
npm test -- --coverage

# Run specific test file
npm test -- ExpenseCalculator.test.ts

# Run in watch mode
npm test -- --watch

# Run E2E tests
npm run test:e2e

リファレンスドキュメント

詳しいパターンと手法については、以下を参照してください。

  • references/testing_patterns.md - 包括的なテストパターン、ベストプラクティス、コード例
  • references/bug_analysis.md - 詳細なバグ分析フレームワーク、一般的なバグのパターン、デバッグ手法

これらのリファレンスには、豊富な例と高度な手法が含まれています。以下の場合に読み込んでください。

  • 複雑なテストシナリオに対処する場合
  • 特定のパターンの実装が必要な場合
  • まれな問題をデバッグする場合
  • 特定の状況におけるベストプラクティスを探す場合

スクリプト

analyze_coverage.py

Jest/Istanbul のカバレッジレポートを分析して、不足箇所を特定します。

python3 scripts/analyze_coverage.py [coverage-file]

指定されていない場合は、一般的なカバレッジファイルの場所を自動的に検索します。

出力:

  • カバレッジしきい値を下回るファイル
  • ステートメント、分岐、関数のカバレッジ率
  • 改善を優先すべきファイル
find_untested_code.py

対応するテストファイルがないソースファイルを検索します。

python3 scripts/find_untested_code.py [src-dir] [--pattern test|spec]

出力:

  • ソースファイルとテストファイルの合計数
  • テストファイルのカバレッジ率
  • 種類別(API、サービス、コンポーネントなど)に分類された未テストファイル
  • 優先順位付けの推奨事項

ベストプラクティスのまとめ

  1. 新しい機能を追加する際はまずテストを書く(TDD)
  2. 実装ではなく動作をテストする — リファクタリング後もテストが有効であること
  3. テストを独立させる — テスト間で状態を共有しないこと
  4. 説明的な名前を付ける — テスト名でシナリオが分かるようにすること
  5. エッジケースを網羅する — null、空、境界値、エラー条件
  6. 外部依存関係をモックする — テストを高速かつ信頼性の高いものにすること
  7. 高いカバレッジを維持する — 重要なコードでは 80% 以上
  8. 失敗するテストをすぐに修正する — 壊れたテストをコミットしないこと
  9. テストをリファクタリングする — 本番コードと同じ品質基準を適用すること
  10. ツールを活用する — カバレッジ分析と不足箇所の特定を自動化する

コメント