test-specialist について
JavaScript/TypeScript アプリケーション向けのテストスキルです。テスト設計、体系的なデバッグ、カバレッジ分析に加え、未テストのファイルを見つけるスクリプトを備えています。
主な機能
- 01単体・統合・E2E テストの設計
- 02体系的なバグ再現と根本原因分析
- 03セキュリティ・性能・境界値の確認
- 04カバレッジと未テストファイルの分析スクリプト
- 05テスト保守と品質の実践
活用シーン
- 01TDD で新機能のテストを作成
- 02バグを再現し、修正と回帰を検証
- 03カバレッジ不足や未テストのファイルを特定
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ ailabs-393/ ai-labs-claude-skills --skill test-specialistインストール後、Claude Code で/test-specialistを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
index.jspackage.jsonreferences/bug_analysis.mdtesting_patterns.mdscripts/analyze_coverage.pyfind_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段階の分析プロセス
-
再現:バグを確実に再現します
- 発生させる正確な手順を記録します
- 必要な環境や状態を特定します
- 期待される動作と実際の動作を記録します
-
切り分け:問題の範囲を絞り込みます
- コードパスを二分探索します
- 最小限の再現ケースを作成します
- 無関係な依存関係を取り除きます
-
根本原因分析:根本的な原因を特定します
- 実行フローを追跡します
- 前提条件や事前条件を確認します
- 最近の変更を確認します(git blame)
-
修正の実装:解決策を実装します
- まず失敗するテストを作成します(TDD)
- 修正を実装します
- テストが成功することを確認します
-
検証:問題が完全に解決されたことを確認します
- テストスイート全体を実行します
- エッジケースをテストします
- リグレッションがないことを確認します
よくあるバグのパターン
競合状態:
// 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);
});
ワークフローの判断ツリー
この判断ツリーに従って、テスト方法を決定してください。
-
新しい機能を追加しますか?
- はい → まずテストを作成します(TDD)
- 失敗するテストを書く
- 機能を実装する
- テストが成功することを確認する
- リファクタリングする
- いいえ → ステップ 2 に進む
- はい → まずテストを作成します(TDD)
-
バグを修正しますか?
- はい → バグ分析プロセスを適用する
- バグを再現する
- バグを示す失敗するテストを書く
- 実装を修正する
- テストが成功することを確認する
- いいえ → ステップ 3 に進む
- はい → バグ分析プロセスを適用する
-
テストカバレッジを改善しますか?
- はい → カバレッジツールを使用する
find_untested_code.pyを実行して不足箇所を特定する- カバレッジレポートに対して
analyze_coverage.pyを実行する - 重要なパスを優先する
- 未テストのコードに対するテストを書く
- いいえ → ステップ 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、サービス、コンポーネントなど)に分類された未テストファイル
- 優先順位付けの推奨事項
ベストプラクティスのまとめ
- 新しい機能を追加する際はまずテストを書く(TDD)
- 実装ではなく動作をテストする — リファクタリング後もテストが有効であること
- テストを独立させる — テスト間で状態を共有しないこと
- 説明的な名前を付ける — テスト名でシナリオが分かるようにすること
- エッジケースを網羅する — null、空、境界値、エラー条件
- 外部依存関係をモックする — テストを高速かつ信頼性の高いものにすること
- 高いカバレッジを維持する — 重要なコードでは 80% 以上
- 失敗するテストをすぐに修正する — 壊れたテストをコミットしないこと
- テストをリファクタリングする — 本番コードと同じ品質基準を適用すること
- ツールを活用する — カバレッジ分析と不足箇所の特定を自動化する